Files
clean-architecture-backend-…/docs/web/advanced-capabilities.md
T

60 lines
4.6 KiB
Markdown

# Web Advanced: support matrix
Every capability is off unless named. `WebAdvancedPromotionGate.forFeature` is the machine-checked
form of the last two columns; if this table disagrees with it, the code wins.
| Capability | Flag | What it adds | Required suites | Soak |
| --- | --- | --- | --- | --- |
| `MVC_VIRTUAL_THREADS` | `backend.web.advanced.mvc-virtual-threads.enabled` | A different scheduling model for **every** request | `web:test`, `webCrossStackParityTest`, `virtual-thread-admission`, `pinning-jfr` | 24h |
| `WEBFLUX_BLOCKING_BRIDGE` | `…webflux-blocking-bridge.enabled` | A bounded offload; affects the shared event loop | `web:test`, `blocking-bridge-bounded`, `event-loop-guard` | 24h |
| `JSON_MERGE_PATCH` | `…json-merge-patch.enabled` | RFC 7396, partial documents | `web:test`, `patch-security`, `patch-atomicity` | 8h |
| `JSON_PATCH` | `…json-patch.enabled` | RFC 6902, arbitrary pointers | as merge patch | 8h |
| `SSE` | `…sse.enabled` | Long-lived connections | `web:test`, `streaming-soak-10k`, `slow-consumer-bounded`, `cancellation-propagation`, `pod-drain` | 24h |
| `NDJSON` | `…ndjson.enabled` | Long-lived connections | as SSE | 24h |
| `JSON_SEQUENCE` | `…json-sequence.enabled` | Long-lived connections | as SSE | 24h |
| `FUNCTIONAL_WEBFLUX` | `…functional-webflux.enabled` | Routes with no annotation to scan | `web:test`, `functional-route-parity` | 8h |
| `CBOR` | `…cbor.enabled` | A second decoder | `web:test`, `codec-security`, `codec-budget` | 8h |
| `XML` | `…xml.enabled` | A decoder with dangerous defaults | as CBOR | 8h |
| `OPENAPI_32` | `…openapi-32.enabled` | A parallel description artifact | `web:test`, `openapi-32-toolchain-matrix` | 8h |
| `RATELIMIT_DRAFT_HEADERS` | `…ratelimit-draft-headers.enabled` | Additive response headers | `web:test`, `ratelimit-draft-headers` | 8h |
## Two capabilities change requests that do not use them
`WebAdvancedFeature.affectsUnrelatedRequests()` is true for exactly `MVC_VIRTUAL_THREADS` and
`WEBFLUX_BLOCKING_BRIDGE`. A codec affects only requests that negotiate it; a virtual-thread
executor affects every request in the process, and the blocking bridge affects the event loop they
all share. `WebAdvancedFeatureFlags.stableBehaviourPreserved()` reports whether either is on.
That distinction is why those two soak for 24 hours and why their rollback test is the one that
matters most.
## What is refused, and why the row is here
| Refused | Reason |
| --- | --- |
| Virtual threads without an admission limit | The pool size *was* the admission policy. Removing it accepts every arrival and queues them on downstream budgets that did not grow. |
| A blocking offload for an unregistered operation | `boundedElastic()` is reachable from anywhere and unbounded in practice; an unenumerable set of offloads is invisible until the pool is the heap. |
| A merge patch outside its field allowlist | A merge patch is partial, so no DTO's absent fields say "not permitted". Without an allowlist the writable set grows every time somebody adds a field. |
| A JSON Patch pointer above its allowed prefix | Replacing a parent deletes every sibling, so permission on a child cannot grant it. |
| A `move` whose source is unauthorized | Checking only the destination lets a caller relocate data out of a field they may not touch. |
| XML with a DTD or an external entity | Billion-laughs and XXE. Neither errors when it fires; the parse succeeds and the document contains something it should not. |
| A non-JSON representation without all three gates | Flag, route `produces`, and client allowlist. An `Accept` header is not evidence the route was tested against that codec. |
| Compression-style silent fallback on a 406 | A client that asked only for CBOR and gets JSON parses the bytes as CBOR and fails somewhere far away. |
| Changing HTTP status after commit | The body becomes half stream, half JSON, and the 200 is cached. |
| Unbounded buffering for a slow consumer | It moves the client's slowness into the server's heap. |
## Promotion
Each capability is promoted on its own evidence. Two conditions apply to all and are not waivable:
- **Rollback exercised.** A flag nobody has turned off is not known to turn off.
- **Stable behaviour unchanged with the feature off.** If it is not, the feature was never optional
and every deployment has it.
Parsers and patch appliers additionally require a security review —
`WebAdvancedPromotionGate.needsSecurityReview` names them.
See `docs/adr/ADR-WEB-ADV-001-streaming-is-live-delivery.md`,
`ADR-WEB-ADV-002-virtual-threads-do-not-remove-admission.md`, and
`ADR-WEB-ADV-003-openapi-32-remains-experimental.md`.