Skip to content

Release 7.0.21 GA: promote beta with CI gates - #1087

Merged
justcoding121 merged 42 commits into
stablefrom
beta
Oct 10, 2026
Merged

justcoding121 merged 42 commits into
stablefrom
beta

Conversation

@justcoding121

Copy link
Copy Markdown
Owner

Promotes beta to stable for the 7.0.21 GA cut. No second version bump. Product code is 347a9a1b.

Already shipped in the beta

  • Windows CLI is a single self-contained file, so winget Links shortcuts for titanium and twp can start.
  • HTTP/3 over-cap DATA streams on the fast-forward path.
  • Inspector Request and Response tabs.
  • Proxy error hardening: 502/504 before commit.

Gate already run

Local winget install of the v7.0.21-beta zip. titanium and twp both resolve through %LOCALAPPDATA%\Microsoft\WinGet\Links and print 7.0.21.

Winget-pkgs pull requests wait until the stable assets and SHA256SUMS exist.

justcoding121 and others added 30 commits October 6, 2026 23:53
…ty cache folders

Clear sessions deletes the current run, including rows the list limit already hid. Session retention can wipe every saved run after confirmation, and empty run folders are removed instead of accumulating.
The capture window belongs on the status bar for any non-empty list, not only after retention has dropped older rows.
…uieter expected failures (#1083)

* Harden proxy error paths: origin 502/504 before commit, client-reject breaker, log accuracy

- Answer 502/504 on H1 origin failure before the client response is committed
- Circuit breaker + decrypt bypass for clients that reject the MITM certificate
- Timeout log names the configured limit; QUIC shutdown noise at Trace
- Bridges map ProxyTimeoutException to 504
- Regression-2026-10-07 tests

* Replay stale pooled H2-to-H1 bridge requests once; SKI/AKI on new roots; stale root cleanup

- H2->H1.1 bridge replays a bodiless idempotent request once on a fresh connection when a reused origin socket was closed
- New roots carry SKI; leaves carry AKI only when the issuer has an SKI
- Inspector: Remove old root CAs command (same-CN roots except the current one)
- H3 origin session dispose and H2 relay origin-death regression tests (no defect reproduced)
- Troubleshooting docs for 502/504 and client-reject bypass

* ci: strict Regression-2026-10-07 gate without flake retry

* fix(inspector): date the since time, fix its tests, and stop repeating the host in the URL column

Showing since on every non-empty list made HH:mm ambiguous once a capture crossed midnight, so add the date when the oldest session is not from today. The previous commit also broke SessionPipelineTests, which still expected a bare Sessions: N, and added a redundant count refresh after Session retention.

The URL column repeated the Host column. Show path and query instead (non-default port kept, CONNECT shows the port), with the full URL in the tooltip, Ctrl+C, sorting and search.

* Inspector: show/hide session grid columns from header menu and Options > Columns

Add a column chooser (header right-click and Options > Columns) with Reset columns. New optional Started, Scheme and Content-Type columns are hidden by default; URL always stays visible. Visibility persists immediately in the saved grid layout, hiding the sorted column falls back to Id ascending, and the default grid is unchanged.

* fix(proxy): one-line logs for expected failures, per-host abort throttle, CONNECT 502 for opaque tunnels

Replay of 100 rapid tabs + sanity browse after the earlier hardening showed
the log was dominated by repeated stack traces for expected failures
(9.6 MB, 6.3 MB of it 'Client session cancelled') and opaque tunnels still
answered CONNECT 200 then silently closed on DNS/refusal.

- ProxyDiagnostics: expected exceptions log one Debug summary line; the
  stack moves to Trace. ReportCaught applies the same rule.
- Happy Eyeballs and 'connection setup FAILED' lines: summary at Debug.
- ProxyLog.ClientHandshakeAborted: per-host 10 s throttle (bounded table).
- ExplicitClientHandler: an opaque tunnel reuses the connection dialed for
  EstablishServerConnectionBeforeResponse instead of dialing twice.
- Inspector: opaque tunnels dial before CONNECT 200 (browser shows
  ERR_TUNNEL_CONNECTION_FAILED with a 502 instead of a bare close).
- Tests: Regression-2026-10-07 log-shape, throttle, opaque 502 and reuse.
- Docs: troubleshooting (website + wiki).

* Inspector: keep following the latest session after adding or clearing a filter

The DataGrid resets its scroll offset when the session list is swapped for a filter change. That was read as the user scrolling away, which paused follow-latest and left the grid mid-list. Ignore scroll changes during a list swap and resume follow when the list is empty or fits on screen.

* Inspector: empty URL cell for CONNECT tunnels; Host and URL always visible

A CONNECT target has no path, so the URL cell is empty (host:port stays in the tooltip) instead of a stray :443. Because URL shows only the path and Host shows the host, both columns are pinned in the column chooser. Host column default width is wider so long names do not run into the next column.

* Proxy: surface origin truncation and honour response-header timeout on fast paths

A short Content-Length read from the origin no longer rewrites the length to look complete: the fast paths keep the declared length and close the client connection, and the HTTP/2 bridge fails the exchange (502 or RST_STREAM). The H1 fast path and the HTTP/2 bridge now apply ResponseHeaderTimeoutSeconds and the per-session timeout, so a stalled origin yields a 504 instead of hanging. Adds origin-failure integration tests with a scripted TLS origin.

* Fail short-body copies, add H3-dead, ALPN-reject and stale-root regression tests, document truncation

* tests: use X509CertificateLoader (SYSLIB0057 under warnaserror)

---------

Co-authored-by: justcoding121 <justcoding121@users.noreply.github.com>
…pt the firewall

Wildcard UDP and TCP binds raise the Windows inbound prompt. Loopback QUIC listens on both ::1 and 127.0.0.1, and dual-listen retries long enough to leave a UDP exclusion range.
…arity, connect-timeout log, openssl verify and stale-root cleanup tests; gate Inspector regression tests
…oes not collide

A self-signed root that shares a subject with another certificate in the Windows store makes X509Chain.Build throw, so the HTTPS handshake fails before the client sees a certificate.
Start() only rolls back listeners it has already published, so a ::1 socket left assigned after the 127.0.0.1 bind throws stayed open and held the port.
…g-gaps

Proxy error hardening follow-up: bypass reason, remaining regression tests, loopback test listeners
…IPv6

Loopback QUIC endpoints bind ::1 first, which cannot work without IPv6. Fall back to the single configured address there, and correct the RPS probe comment that still described the old IPv6Any rewrite.
Pace capture-driven grid updates with a time-budgeted, adaptive flush at Background priority, coalesce per-session change notifications, defer flushes while the user is interacting, run the UI thread at AboveNormal, and make SessionStore body-byte accounting O(1) per add/update instead of a full rescan on the UI thread.
…ht, add replay probe

- Pace capture and notifications only when the classic desktop lifetime owns the UI loop; headless test sessions keep the synchronous path (deferred notifications made proxy-starting tests flaky).
- Fixed DataGrid RowHeight (same 33px as auto) removes per-row height measurement; flood p99 dropped from ~390 ms to ~290 ms and max from 1.8 s to 1.2 s.
- Add the replay probe scenario (sustained load plus optional headless browsers, UI latency sampler).
- Cover the QUIC loopback IPv4-bind-failure rollback and remove a thread-id flake in LowerDiskBudget test.
The quality gate was failing on a Task.Delay with no cancellation token. The other open smells were parameter count, identical branches, complexity, and analyzer nits. DocFX wrote the search query and an aria-controls value into hrefs; the publish step reapplies those fixes after regeneration.
Sonar's MSTEST0037 wants IsTrue for this nullable bool, not AreEqual.
MSTEST0037 flags both IsTrue(x == true) and AreEqual(true, x). Asserting the nullable bool's value avoids both.
GetResponseBody on an H2->H3 bridged response fell into the HTTP/1.x
reader and threw "Connection is null" (the body lives on the QUIC
stream). The Inspector swallowed it, so rows kept only the client
protocol and had no status.

- Add Response.Http3RawBodyDrain so SessionEventArgs can buffer the
  QUIC body (wire bytes, no body-write hook, bounded by
  MaxBufferedBodyBytes) and release the origin stream.
- Inspector: do not read Body on bodiless responses (BodyNotFound),
  keep status/protocol when body capture fails, and trace failures.
- Add regression test asserting the handler itself sees the body.
Replace the combined Headers, Body, and Hex tabs with separate request and response header and body tabs. Pretty and Hex are checkboxes on the body tabs.
…ONNECT, hex fallback for text-only bodies, value-compared body cache, remember last core tab
…ved' with reason; record synthetic 502/504; E2E never opens OS cert UI
…fast path instead of an empty session; lock request early in H2->H3 bridge so BeforeResponse GetResponseBody works
… drop a discovery-file change that arrives mid-apply, and load Plus assemblies from bytes.
justcoding121 and others added 12 commits October 9, 2026 00:05
Four separate tabs made it hard to read one message, and they crowded Diff, WebSocket, SSE, and Protobuf. A splitter keeps both panes readable, and a short window collapses headers so the body keeps the space.
… headers with LINQ, and use boolean asserts.
…boxes shrink on short windows.

At 280px the Save button covered the Hex checkbox, so the pane now stays at 360px or wider. The headers and body rows also drop their minimum heights so each box scrolls inside itself instead of the pane clipping its bottom.
…elay unit.

The Windows arms that missed the peer floor on CI did not reproduce here, and a 64 KiB relay trial did not lift the noisy slow-consumer arm, so the size stays 16 KiB. Decode chunked HTTP/1.1 bridge bodies in the translation test that CI failed twice.
The .NET apphost does not follow the WinGet Links symlink to find titanium.dll. A single-file win-x64 publish removes that lookup, and the release workflow fails if twp or titanium cannot print a version through those symlinks.
…ll OS

Reason: the 7.0.21 candidate suite at 347a9a1 was green in 10 of 11 modes.
The only red leg was the Windows H3 cleartext slow-consumer arm (256 KiB,
client sleeps per 16 KiB), TWP / closest peer 0.460. Root-cause analysis found
no Titanium-specific cause and no YARP architecture advantage: on a 4-vCPU
Windows host the arm saturates the MsQuic/loopback UDP datapath for both
proxies (about 70-100k datagrams/s, ~290% of one core in the proxy, three
quarters kernel), datagrams per request vary 811-1689 inside one proxy, and
results are bimodal for TWP and YARP alike, so a median of 3 repeats swings
between ~0.46 and ~1.0. Local 5-repeat ratio 0.99, 11 pooled runs 133 vs 135
RPS, Linux same arm parity (TWP 463/465/458 vs YARP 456/457/459).

A single-write H3 frame header+payload micro-optimization was tried and
reverted: worse on the target arm (median 46.6 vs 116.5 RPS, 5 of 6 pairs).

Unchanged: MITM Lite/Full / Reverse 0.25 and edition ratio floors 0.50.
Validated: all 234 leg-runs of the suite CSVs pass at 0.40; at 0.50 exactly
that one Windows leg fails. Reason also recorded in PERF-GATES.md.
Every Performance.md table, the six practical charts and the run links now come
from the 11-mode suite on 347a9a1 (product 37894265694, saturation
37894269584, bodies 37894273314, POST 37894276720, lossy 37894280112, arch
37894283777, TLS cost 37894288020, gRPC 37894294007, WebSocket 37894297605 and
37894301831, editions 37894305384). Cells are the measured numbers.

Verdicts seen before this commit: 234 of 234 leg-runs pass the peer gates at
0.40 (at 0.50 only Windows H3 cleartext slow-consumer fails, 0.460). The three
Windows rows that were under 0.50 at 482f598 now read 1.42 (H3 to H2 TLS slow
256 KiB) and 0.91 (H3 to H1 TLS slow 256 KiB); H3 to H1 TLS body 256 KiB passes.
Titanium absolute RPS vs 482f598 is flat by median on every OS; macOS peer
cells moved more than Titanium did (YARP run-to-run 0.24x-2.73x), so the macOS
ratio drops are runner variance. Titanium RSS median +1-2%.

The historical v2 MITM note stays at 41f4ade.
The 7.0.21 promote ran the same tree as the published beta and failed Ubuntu ui-portable at +273MB RSS after the in-memory body counter had passed. Linux VmRSS is not returned after GC.
@sonarqubecloud

sonarqubecloud Bot commented Oct 9, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
77.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@justcoding121
justcoding121 merged commit 66587c1 into stable Oct 10, 2026
34 of 46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant