Commits
Tarun Sukhani authored 1b041e0746c
feat: add built-in CORS support via CorsPlugin (PF-6) Adds a new opt-in CorsPlugin that handles cross-origin requests at the framework level so apps no longer need bespoke CORS interceptors. Design: - Disabled by default (cors.enabled=false). Apps must explicitly opt in to avoid changing the response shape of existing deployments. - Preflight short-circuit happens in rawInvocation, which fires before the action invoker — OPTIONS requests with a recognized Origin return 204 + the configured CORS headers without ever dispatching to a controller. Hooks like onActionInvocationResult would be too late for that. - Header writes on regular responses go through a setHeader-if-absent helper, so CorsPlugin coexists with PF-5's SecurityHeadersPolicy and with application code that already sets these headers — neither side blows the other away. - Wildcard + credentials safety: per the CORS spec, * Access-Control-Allow-Origin is incompatible with Access-Control-Allow-Credentials: true. When both are configured the plugin reflects the request Origin header instead of *, and logs a WARN at boot. - Specific allowedOrigins entries are matched case-sensitively, the matched origin is echoed back, and Vary: Origin is appended (preserving existing Vary entries) so caches don't cross-serve responses across origins. Slot 15: in framework/src/play.plugins — slot 10 is SecurityHeadersPlugin (PF-5), slot 100 is TempFilePlugin; 15 keeps CORS adjacent to security headers without disturbing existing slots. Tests: - framework/test-src/play/plugins/CorsPluginTest.java covers the AC: off by default, allow-origin on enabled simple requests, OPTIONS short- circuit returning 204 with all preflight headers, wildcard+credentials reflection, exposed-headers passthrough, origin reflection from a specific allow-list, plus negative cases (disallowed origin not echoed, no Origin header → no CORS, plugin leaves app-set headers untouched, Vary appends to existing). - framework/test-src/integration/CorsFunctionalTest.java exercises the plugin end-to-end against the real Netty server in test-src/integration/testapp/, with the testapp's application.conf configured for cors.allowedOrigins=https://example.com. - PluginCollectionTest.verifyLoading updated to include CorsPlugin in the expected plugin list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>