feat: web, websocket 어댑터 추가 구현
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user