Repository navigation
Performance
Titanium targets low-overhead MITM proxying: connection pooling, HTTP/2 multiplexing, and buffer reuse. Numbers below are Release measurements with RpsLoadProbe (and BenchmarkDotNet / Basic example where noted). Publishable tables cite GitHub Actions medians on matched 4 vCPU / 16 GiB runners. Absolute RPS still varies by OS kernel, TLS, and MsQuic packaging — compare within a table, not across Windows vs Linux.
Control arms: nginx (native C reverse-proxy ceiling; Linux is authoritative) and YARP (Yarp.ReverseProxy, managed .NET reverse proxy). Neither can MITM (no CONNECT / forged certs). FiddlerCore is not compared (commercial debugger license; not a throughput peer).
For pooling knobs and certificate first-visit tuning, see Performance and pooling. For the local cool A/B lab, laptop tables, and the techniques used to find each hotspot, see Performance Profiling.
Both OS use the standard public-repo GitHub-hosted runner class (4 vCPU / 16 GiB / 14 GB SSD). Same harness knobs (workflow_dispatch RPS saturation: warmup 2s / measure 8s; concurrency 8, 16, 32, 64; median of 3 repeats). Prefer TWP÷YARP / TWP÷nginx ratios over absolute RPS.
Laptop High-perf / cool-paired Windows numbers live on Performance Profiling — Local Windows lab. Do not mix those absolutes into the tables below.
| OS | Windows Server (GitHub-hosted windows-latest) |
| CPU | 4 logical processors |
| RAM | 16 GiB |
| Runtime | .NET 10.0.x |
| nginx | nginx/Windows 1.31.3 (same-OS only; no QUIC) |
| YARP | Yarp.ReverseProxy 2.3.0 |
| Harness | RpsLoadProbe Release; median of 3 repeats |
| OS | Ubuntu 24.04.x LTS |
| CPU | 4 logical processors (AMD EPYC; runners this pass were 7763 / 9V74) |
| RAM | 16 GiB |
| Runtime | .NET 10.0.11 |
| nginx | nginx/1.31.4 (nginx.org mainline, --with-http_v3_module) |
| YARP | Yarp.ReverseProxy 2.3.0 |
| Harness | RpsLoadProbe Release; median of 3 repeats where noted |
How to read the tables
- Mode: Reverse = transparent fixed-forward (may TLS-terminate to a cleartext origin, or re-encrypt to a configured HTTPS/QUIC origin). MITM = both legs are visible in the clear inside TWP — either by decrypting client TLS/QUIC (forged cert / CONNECT) or by accepting an already-cleartext client (explicit HTTP proxy / inspectable transparent reverse) while still speaking plain or TLS to the origin. nginx and YARP cannot do MITM. HTTP/3 has no cleartext client (QUIC always encrypted).
- Sustainable = last concurrency that still met error/latency SLOs. Peak = highest RPS in that ramp.
- 🟢 = highest sustainable RPS among products that have a number on that row. Omitted when only TWP can run the path (no fair multi-product comparison).
- Not possible = product cannot do that path. Not measured = path exists but no published number yet for that OS.
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-same
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-terminate
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-bridges
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-mitm
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-bodies
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-post
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-lossy
pwsh tools/RpsLoadProbe/run-rps.ps1 -Mode compare-tls-costClient / origin: HTTP version and whether TLS is used (plain = cleartext, TLS = encrypted, QUIC = HTTP/3).
Median of 3 repeats on windows-latest (4 vCPU / 16 GiB). After a cool local win, run RPS saturation (compare-same, compare-bridges, compare-mitm) and paste medians + the Actions run ID here. No Windows CI matrix has been pasted yet — cells below stay Not measured until that run. Laptop High-perf / cool-paired numbers stay on the local lab.
Load generators: Reverse inbound H3 arms use dotnet-httpclient (http_version=3.0, RequestVersionExact) after dual-listen reverse H3. nginx/Windows is same-OS only (no QUIC).
| Mode | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|
| Reverse | HTTP/1 · plain | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| Reverse | HTTP/1 · plain | HTTP/1 · TLS | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
| Reverse | HTTP/1 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| Reverse | HTTP/1 · TLS | HTTP/2 · TLS | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
| Reverse | HTTP/1 · TLS | HTTP/3 · QUIC | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| Reverse | HTTP/2 · plain | HTTP/1 · plain | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
| Reverse | HTTP/2 · plain | HTTP/2 · plain | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
| Reverse | HTTP/2 · plain | HTTP/2 · TLS | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
| Reverse | HTTP/2 · plain | HTTP/3 · QUIC | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| Reverse | HTTP/2 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| Reverse | HTTP/2 · TLS | HTTP/2 · plain | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
| Reverse | HTTP/2 · TLS | HTTP/3 · QUIC | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| Reverse | HTTP/3 · QUIC | HTTP/1 · plain | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| Reverse | HTTP/3 · QUIC | HTTP/2 · TLS | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| Reverse | HTTP/3 · QUIC | HTTP/3 · QUIC | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| MITM | HTTP/1 · plain | HTTP/1 · plain | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/1 · plain | HTTP/1 · TLS | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/1 · TLS | HTTP/1 · TLS | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/2 · plain | HTTP/2 · plain | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/2 · plain | HTTP/2 · TLS | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/2 · TLS | HTTP/1 · TLS | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/2 · TLS | HTTP/2 · TLS | Not measured | Not measured | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/3 · QUIC | HTTP/1 · TLS | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/3 · QUIC | HTTP/2 · TLS | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/3 · QUIC | HTTP/3 · QUIC | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not possible (no MITM) | Not possible |
Median of 3 repeats. 2026-08-22 post-Windows-parity remasure on develop @ 193d5101 (library unchanged since H3→H1 chunked-drain / QPACK lowercase / H2 header-decode): 32555967423 (compare-same), 32555968862 (compare-bridges). Absolute RPS on this GHA pass is lower than the earlier same-day remasure (32552296839 / 32552295495) — TWP÷YARP ratios hold; prefer ratios over absolutes across VMs. MITM rows still from 32334395907; nginx HTTP/3 tiny-GET peak note from 32337905168. Warmup 2s / measure 8s; concurrency 8, 16, 32, 64. Linux nginx is the authoritative nginx baseline. The RPS workflow installs nginx.org mainline (http_v3_module) and libmsquic (QuicListener.IsSupported=true on ubuntu-latest).
TWP÷nginx H1 plain reverse ≈ 0.73 (27,449 / 37,656); TWP÷YARP H1 plain ≈ 0.96 (27,449 / 28,447). Prefer ratios over absolute RPS on GHA VMs.
| Mode | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|
| Reverse | HTTP/1 · plain | HTTP/1 · plain | 27,449 | 27,449 | 🟢 37,656 | 37,656 | 28,447 | 28,447 |
| Reverse | HTTP/1 · TLS | HTTP/1 · plain | 20,203 | 20,203 | 🟢 28,282 | 28,282 | 20,678 | 20,678 |
| Reverse | HTTP/1 · TLS | HTTP/2 · TLS | 🟢 29,290 | 29,290 | Not possible | Not possible | 28,702 | 28,702 |
| Reverse | HTTP/1 · TLS | HTTP/3 · QUIC | 20,081 | 20,081 | Not possible (no H3 origin) | Not possible | 🟢 21,054 | 21,054 |
| Reverse | HTTP/2 · plain | HTTP/1 · plain | 🟢 40,559 | 40,559 | Not possible | Not possible | 39,878 | 39,878 |
| Reverse | HTTP/2 · plain | HTTP/2 · plain | 🟢 64,883 | 64,883 | Not possible | Not possible | 49,354 | 49,354 |
| Reverse | HTTP/2 · plain | HTTP/2 · TLS | 🟢 54,002 | 54,002 | Not possible | Not possible | 41,158 | 41,158 |
| Reverse | HTTP/2 · plain | HTTP/3 · QUIC | 30,525 | 30,525 | Not possible (no H3 origin) | Not possible | 🟢 30,610 | 30,610 |
| Reverse | HTTP/2 · TLS | HTTP/1 · plain | 🟢 52,431 | 52,431 | 13,396 | 18,791 | 29,371 | 29,371 |
| Reverse | HTTP/2 · TLS | HTTP/2 · plain | 🟢 65,325 | 65,325 | Not possible | Not possible | 45,414 | 45,414 |
| Reverse | HTTP/2 · TLS | HTTP/3 · QUIC | 🟢 29,096 | 29,096 | Not possible (no H3 origin) | Not possible | 26,569 | 26,569 |
| Reverse | HTTP/3 · QUIC | HTTP/1 · plain | 🟢 22,521 | 22,521 | 0 | 18,996 | 21,831 | 21,831 |
| Reverse | HTTP/3 · QUIC | HTTP/2 · TLS | 🟢 28,850 | 28,850 | Not possible (no H3→H2) | Not possible | 24,902 | 24,902 |
| Reverse | HTTP/3 · QUIC | HTTP/3 · QUIC | 🟢 20,621 | 20,621 | Not possible (no H3 origin) | Not possible | 16,836 | 16,836 |
| MITM | HTTP/1 · TLS | HTTP/1 · TLS | 30,703 | 30,703 | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/2 · TLS | HTTP/1 · TLS | 38,764 | 38,764 | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/2 · TLS | HTTP/2 · TLS | 52,372 | 52,372 | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/3 · QUIC | HTTP/1 · TLS | 24,036 | 24,036 | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/3 · QUIC | HTTP/2 · TLS | 26,746 | 26,746 | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
| MITM | HTTP/3 · QUIC | HTTP/3 · QUIC | 22,346 | 22,346 | Not possible (no MITM) | Not possible | Not possible (no MITM) | Not possible |
On this GHA shape, TWP H1 plain ÷ nginx H1 plain ≈ 0.73 (27,449 / 37,656). H1 TLS terminate ≈ 0.71 (20,203 / 28,282). TWP÷YARP H1 plain ≈ 0.96; H1 TLS terminate ≈ 0.98. Absolute RPS swings by VM; prefer the ratio and median across repeats.
nginx HTTP/3: inbound QUIC terminate → cleartext H1 is in the harness (nginx-reverse-http3-cleartext, nginx.org 1.31.4). HttpClient/MsQuic negotiates 3.0 and peaks at ~19k RPS, but the error rate stays above the 0.1% SLO (sustain 0). nginx still cannot speak HTTP/3 to an origin (no H3 upstream in this conf), so H3-origin rows stay blank. nginx peak on the H3→H1 row is retained from the older tiny-GET pass.
YARP HTTP/3 (2026-08-22 post-parity remasure): TWP leads H3→H1 ≈ 1.03× (22,521 / 21,831), H3→H2 ≈ 1.16× (28,850 / 24,902), H3→H3 ≈ 1.22× (20,621 / 16,836). H1→H2 ≈ 1.02× (29,290 / 28,702). Near-ties: H1→H3 ≈ 0.95×, h2c→H3 ≈ 1.00×. H2 TLS→H1 YARP looked soft this pass (~29k vs TWP ~52k) — treat that absolute cautiously; ratio still TWP-led.
Windows vs Linux: both CI envs are 4 vCPU / 16 GiB, but do not compare absolute RPS across OS. Linux still has nginx leading H1 plain/TLS terminate; vs YARP, H3 bridges and same-protocol H2 are TWP-led, with H1 plain still ~0.96× YARP. Cool laptop Windows ratios (tiny-GET at parity or better vs YARP) live on Performance Profiling until the first windows-latest median is pasted above.
The tables above use ~64 B keep-alive GET on loopback. On Linux H1 reverse, nginx leads; YARP sits near TWP. Heavier bodies, POSTs, TLS handshake cost, and lossy/HOL workloads (below) change the picture. MITM rows remain TWP-only. nginx HTTP/3 is inbound-terminate only (see note above).
For tiny JSON responses (~64 B) on loopback, that ordering is not expected: topology (TLS hop count, terminate vs MITM) dominates; HTTP/2 and HTTP/3 help multiplexing, not single-origin tiny-GET RPS. See the lossy tables below for a workload where protocol design matters.
Separate from the tiny-GET matrix. Same measurement environments. Modes: compare-bodies, compare-post, compare-lossy, compare-tls-cost in RpsLoadProbe. PUT with the same body is the same proxy work as POST; DELETE with no body matches GET — only POST is published.
Lossy link = userspace shim (not kernel netem): TCP gets per-buffer delay + occasional whole-connection stalls (honest HOL for multiplexed H2); UDP datagram drop exists in the harness but H3 lossy is not published — rechecked at concurrency 8 after H2/H3 streaming work (rps-ramp-20260817-212421): TWP H3 through the UDP shim stayed at 0 sustain (multi-second p99); YARP H3 via the TCP shim also failed to establish. Treat as a measurement limitation (MsQuic + lossy shim), not a capability claim.
Median of 3 repeats on windows-latest. Mode: compare-bodies. Pending first Windows CI paste. Cool laptop remasures stay on the local lab.
| Body | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|
| 64 KiB | HTTP/1 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| 64 KiB | HTTP/2 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| 64 KiB | HTTP/3 · QUIC | HTTP/1 · plain | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
| 256 KiB | HTTP/1 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| 256 KiB | HTTP/2 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| 256 KiB | HTTP/3 · QUIC | HTTP/1 · plain | Not measured | Not measured | Not possible (no QUIC) | Not possible | Not measured | Not measured |
Median of 3 repeats. Source: Actions 32562607744 (compare-bodies on 0726610e — post H2→H1 unbuffered read + 288 KiB flatten). Warmup 2s / measure 8s.
| Body | Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|---|
| 64 KiB | HTTP/1 · TLS | HTTP/1 · plain | 6,618 | 6,618 | 🟢 8,251 | 8,251 | 6,583 | 6,583 |
| 64 KiB | HTTP/2 · TLS | HTTP/1 · plain | 🟢 6,048 | 6,048 | 3,588 | 3,588 | 4,691 | 4,691 |
| 64 KiB | HTTP/3 · QUIC | HTTP/1 · plain | 🟢 4,396 | 4,396 | 1,647 | 1,647 | 4,353 | 4,353 |
| 256 KiB | HTTP/1 · TLS | HTTP/1 · plain | 2,105 | 2,105 | 🟢 2,710 | 2,710 | 2,192 | 2,192 |
| 256 KiB | HTTP/2 · TLS | HTTP/1 · plain | 🟢 1,489 | 1,489 | 903 | 903 | 1,404 | 1,404 |
| 256 KiB | HTTP/3 · QUIC | HTTP/1 · plain | 🟢 4,115 | 4,115 | 415 | 415 | 1,318 | 1,318 |
On this GHA pass TWP÷YARP H1 TLS ≈ 1.01× (64 KiB) / 0.96× (256 KiB); H2→H1 ≈ 1.29× / 1.06×; H3→H1 ≈ 1.01× / 3.12× (YARP soft on 256 KiB H3 — treat absolute cautiously). TWP÷nginx H1 TLS ≈ 0.80 / 0.78. Absolute RPS swings by VM; prefer ratios.
Median of 3 repeats on windows-latest. Mode: compare-post. Pending first Windows CI paste. Laptop 1-rep / cool POST notes stay on the local lab.
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| HTTP/2 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| HTTP/3 · QUIC | HTTP/1 · plain | Not measured | Not measured | Not possible | Not possible | Not measured | Not measured |
Median of 3 repeats. Source: Actions 32335541836 (compare-post).
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain | 3,885 | 3,885 | 🟢 4,041 | 4,041 | 3,112 | 3,112 |
| HTTP/2 · TLS | HTTP/1 · plain | 🟢 2,763 | 2,763 | 1,929 | 1,990 | 2,468 | 2,525 |
| HTTP/3 · QUIC | HTTP/1 · plain | 0 | 2,033 | Not measured | Not measured | 🟢 2,592 | 2,592 |
Linux nginx H1/H2 POST completed this pass (nginx.org 1.31.4; the previous Ubuntu 1.24 arm returned 100% errors). TWP H3 POST peaked at 2,033 but did not hold the error/latency SLO (sustain 0). nginx HTTP/3 POST used the pre-fix IPv4-only QUIC listen and is left unmeasured.
Userspace 5 ms one-way delay + 1% connection stall; 64 KiB GET. Median of 3 repeats on windows-latest. Mode: compare-lossy. Pending first Windows CI paste.
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| HTTP/2 · TLS | HTTP/1 · plain | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
H1 stays usable; H2 collapses under connection stalls (HOL) on every product in the laptop lab. Absolute RPS is low because the shim delays every buffer — the point is the protocol shape, not competing with the tiny-GET table.
Median of 3 repeats. Source: Actions 32335543372 (compare-lossy).
| Client | Origin | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|---|
| HTTP/1 · TLS | HTTP/1 · plain | 1,168 | 1,168 | 🟢 1,220 | 1,220 | 1,217 | 1,217 |
| HTTP/2 · TLS | HTTP/1 · plain | 🟢 40 | 44 | 🟢 40 | 44 | 🟢 40 | 40 |
Same story as Windows: H1 stays usable; H2 falls to tens of RPS for all three products. Tiny-GET H1 leadership does not carry over.
Isolates keep-alive tiny GET vs new connection per request (handshake-dominated) vs keep-alive 256 KiB. Product comparison uses RPS and end-to-end latency; TWP can also capture ClientTlsTiming when TWP_RPS_CAPTURE_TLS=1 (child process) — nginx/YARP have no equivalent hook.
Median of 3 repeats on windows-latest. Mode: compare-tls-cost. Pending first Windows CI paste.
| Workload | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|
| Keep-alive · tiny GET | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| New-connection · tiny GET | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
| Keep-alive · 256 KiB GET | Not measured | Not measured | Not measured | Not measured | Not measured | Not measured |
Median of 3 repeats. Source: Actions 32335545066 (compare-tls-cost).
| Workload | TWP sustain | TWP peak | nginx sustain | nginx peak | YARP sustain | YARP peak |
|---|---|---|---|---|---|---|
| Keep-alive · tiny GET | 18,880 | 18,880 | 🟢 28,245 | 28,245 | 20,746 | 20,746 |
| New-connection · tiny GET | 🟢 1,106 | 1,113 | 1,005 | 1,009 | 957 | 957 |
| Keep-alive · 256 KiB GET | 2,126 | 2,126 | 🟢 2,627 | 2,627 | 2,134 | 2,134 |
Verdict (Linux, authoritative nginx): On keep-alive tiny terminate, TWP is ~0.67× nginx and ~0.91× YARP. On new-connection terminate, TWP is ahead of both (~1.10× nginx, ~1.16× YARP). With 256 KiB bodies, nginx still leads sustain when all three are healthy.
| What | Result |
|---|---|
| HTTPS TTFB vs direct (median, 14 hosts) | Cold ≈ parity (−1 ms); warm −25 ms (proxy faster) |
| HTTP/1 loopback GET (no body intercept) | ~186 µs, ~17.5 KB allocated / request |
| Basic example footprint (Release, after load) | ~74 MB working set · ~24–29 MB private bytes |
dotnet run -c Release --project benchmarks/Titanium.Web.Proxy.Benchmarks -- --filter '*Throughput*'| Benchmark | Setup | Mean | Allocated / op |
|---|---|---|---|
| HTTP/1 GET through proxy | Passthrough | 186 µs | 17.5 KB |
| HTTP/2 multiplexed GETs | 10 concurrent streams | 3.0 ms / batch | ~14 KB / request |
There is no artificial upper clamp on server defaults. Per-endpoint overrides:
| Knob | Scope | Default | Override |
|---|---|---|---|
ProxyServer.MaxCachedConnections |
process, per upstream host | 128 | any ≥ 1 |
ProxyEndPoint.MaxCachedConnections |
endpoint → pool depth for that EP’s sessions | null (use server) | e.g. 256 on reverse EP |
ProxyEndPoint.MaxConcurrentClients |
endpoint admission | null (off) | any ≥ 1 |
ResourceLimits.MaxConcurrentStreamsPerConnection |
H2 streams | 256 | ProxyResourceLimits.Create(...) |
TransparentQuicProxyEndPoint.MaxInboundBidirectionalStreams |
H3 | 100 (probe uses 256) | property on EP |
ForwardCleartext |
transparent TLS terminate | false |
true + decrypt |
proxy.MaxCachedConnections = 512;
proxy.ResourceLimits = ProxyResourceLimits.Create(
/* … */,
maxConcurrentStreamsPerConnection: 1000,
maxCachedConnectionsPerHost: 512,
/* … */);
var ep = new TransparentProxyEndPoint(IPAddress.Any, 443, decryptSsl: true)
{
ForwardHost = "127.0.0.1",
ForwardPort = 8080,
ForwardCleartext = true,
MaxCachedConnections = 256,
GenericCertificateName = "example.com"
};
ep.BeforeSslAuthenticate += (_, a) =>
{
a.UpstreamHttpProtocol = UpstreamHttpProtocol.Http11;
a.AllowHttpProtocolTranslation = true; // HTTP/2 client → HTTP/1 origin
return Task.CompletedTask;
};