Skip to content

Refactor CORS handling into injectable components - #12982

Open
sdelamo wants to merge 20 commits into
5.3.xfrom
extract-cors-apis-52x
Open

Refactor CORS handling into injectable components#12982
sdelamo wants to merge 20 commits into
5.3.xfrom
extract-cors-apis-52x

Conversation

@sdelamo

@sdelamo sdelamo commented Sep 3, 2026

Copy link
Copy Markdown
Member

Extract origin configuration lookup, response decoration, and preflight validation from CorsFilter into dedicated components.

Add a configuration option to disable the CORS filter entirely, preserve deprecated extension points for compatibility, and expand coverage for CORS utilities, preflight validation, and filter configuration.

Related to: micronaut-projects/micronaut-security#2271

Extract origin configuration lookup, response decoration, and preflight validation from CorsFilter into dedicated components.

Add a configuration option to disable the CORS filter entirely, preserve deprecated extension points for compatibility, and expand coverage for CORS utilities, preflight validation, and filter configuration.
@sdelamo
sdelamo requested review from dstepanov and a lite review from Copilot September 3, 2026 08:59
@sdelamo sdelamo added the type: improvement A minor improvement to an existing feature label Sep 3, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in 5.2.0 Release Sep 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are correctness issues (including a route-builder call in a new test that should not compile) and several newly introduced @since tags that don’t match the branch version.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This pull request refactors Micronaut HTTP server CORS handling by extracting CorsFilter responsibilities into injectable strategy components, while adding configuration to disable the filter entirely and expanding test coverage around the new CORS utilities and preflight validation.

Changes:

  • Introduces injectable strategy APIs and default implementations for origin configuration retrieval, preflight validation, and response decoration.
  • Adds micronaut.server.cors.filter.enabled configuration (default enabled) to disable the CORS filter bean entirely.
  • Adds/expands tests for CrossOriginUtil, preflight validation behavior, and filter enablement configuration.
File summaries
File Description
http-server/src/test/java/io/micronaut/http/server/cors/PreflightRequestValidatorTest.java New unit test coverage for preflight validator behavior against routes/headers/private-network.
http-server/src/test/java/io/micronaut/http/server/cors/CrossOriginUtilTest.java New unit tests for CrossOriginUtil matching and annotation-to-config conversion.
http-server/src/test/groovy/io/micronaut/http/server/HttpServerConfigurationSpec.groovy Adds assertions for default-enabled and configurable disabling of the CORS filter bean.
http-server/src/main/java/io/micronaut/http/server/HttpServerConfiguration.java Adds nested cors.filter configuration model to control filter enablement.
http-server/src/main/java/io/micronaut/http/server/cors/PreflightRequestValidator.java New strategy interface for validating preflight requests.
http-server/src/main/java/io/micronaut/http/server/cors/DefaultPreflightRequestValidator.java Default implementation performing method/header/private-network checks and route/static-resource validation.
http-server/src/main/java/io/micronaut/http/server/cors/DefaultCorsResponseDecorator.java Default implementation responsible for writing CORS response headers.
http-server/src/main/java/io/micronaut/http/server/cors/DefaultCorsOriginConfigurationRetriever.java Default implementation resolving route-level CORS config first, then server-level config.
http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java Expands shared CORS utility/matching/conversion logic for reuse across the new components.
http-server/src/main/java/io/micronaut/http/server/cors/CorsResponseDecorator.java New strategy interface for CORS response header decoration.
http-server/src/main/java/io/micronaut/http/server/cors/CorsOriginConfigurationRetriever.java New strategy interface for resolving the applicable origin configuration from origin + route matches.
http-server/src/main/java/io/micronaut/http/server/cors/CorsOriginConfiguration.java Javadoc enhancements for existing CORS origin configuration accessors.
http-server/src/main/java/io/micronaut/http/server/cors/CorsFilter.java Refactors filter to delegate to injected strategy components and adds @Requires gating via configuration.
Review details

Suppressed comments (3)

http-server/src/main/java/io/micronaut/http/server/HttpServerConfiguration.java:927

  • setFilter is marked @since 5.4.0, but this branch’s projectVersion is 5.2.0-SNAPSHOT and the setter is introduced here; please align the @since tag with the branch debut version.
         * @since 5.4.0

http-server/src/main/java/io/micronaut/http/server/HttpServerConfiguration.java:1050

  • The new CorsFilterConfiguration nested type is marked @since 5.4.0, but this branch is 5.2.0-SNAPSHOT and the type is introduced in this PR. Update the @since tag to match the actual debut version.
         * @since 5.4.0

http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java:224

  • getCorsOriginConfigurationForAnnotationMetadataProvider is introduced here but is marked @since 5.4.0. On this 5.2.0-SNAPSHOT branch, the @since tag should be 5.2.0.
     * @since 5.4.0
  • Files reviewed: 13/13 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

}

void addRoute(TestController controller) {
GET(ROUTE, controller);
Comment thread http-server/src/main/java/io/micronaut/http/server/HttpServerConfiguration.java Outdated
Comment thread http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java Outdated
sdelamo and others added 3 commits September 3, 2026 11:14
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@sdelamo
sdelamo requested a review from graemerocher September 3, 2026 10:14
Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.
Throw UnsupportedOperationException from the deprecated CorsFilter header mutation methods because CORS response headers are now populated by CorsResponseDecorator.

> Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation.
Set Access-Control-Request-Private-Network to "true" instead of the requested HTTP method when building CORS preflight requests.

@dstepanov dstepanov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the extraction against the original CorsFilter logic method by method — DefaultCorsOriginConfigurationRetriever, DefaultPreflightRequestValidator and DefaultCorsResponseDecorator reproduce the previous behaviour faithfully, including the route-level → server-wide fall-through and the !corsConfiguration.isEnabled() short-circuit. The two deliberate behaviour changes both look like fixes: (AnnotationMetadata) rmrm.getAnnotationMetadata(), and gating Access-Control-Allow-Private-Network on .filter(Boolean.TRUE::equals) rather than header presence — which is what makes the TCK change meaningful, since that assertion was previously vacuous.

Three things worth a look, inline. Two non-blocking notes as well:

  • The new public SPIs (CorsResponseDecorator, PreflightRequestValidator, CorsOriginConfigurationRetriever) point @DefaultImplementation at package-private @Internal classes, so a consumer can replace them but cannot subclass or delegate to the defaults — the protected methods on DefaultCorsResponseDecorator are unreachable from outside the package.
  • micronaut.server.cors.filter.enabled gets no entry in src/main/docs/guide/httpServer/serverConfiguration/cors.adoc, which is where the distinction from cors.enabled most needs explaining.

Comment thread http-server/src/main/java/io/micronaut/http/server/HttpServerConfiguration.java Outdated
@graemerocher

Copy link
Copy Markdown
Contributor

I agree this is a breaking change and can't go into a minor release

Route default CORS response decoration through the deprecated protected mutators so existing CorsFilter subclasses can continue customizing headers, while preserving custom CorsResponseDecorator behavior. Delegate the default mutators to DefaultCorsResponseDecorator, handle filters without a router, and add coverage for standard and short-circuited preflight responses.
@sdelamo

sdelamo commented Sep 4, 2026

Copy link
Copy Markdown
Member Author

@graemerocher the PR should not longer be a breaking change.

*
* @since 5.2.0
*/
@ConfigurationProperties("filter")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the namespace here just "filter" and not "cors-filter"?

return null;
}
for (RouteMatch<?> routeMatch : routeMatches) {
Optional<CorsOriginConfiguration> originConfigurationOptional =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional adds overhead and this looks like some kind of internal API call so seems unnecessary

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved via 376917d

* @author Sergio del Amo
* @since 3.9.0
*/
public final class CrossOriginUtil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review this class and see what needs to be internal / non-public

adding lots of what seem to be internal focused methods to the public API surface doesn't seem wise

Comment thread http-server/src/main/java/io/micronaut/http/server/cors/CrossOriginUtil.java Outdated
@sdelamo
sdelamo changed the base branch from 5.2.x to 5.3.x September 10, 2026 13:45
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: improvement A minor improvement to an existing feature

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

4 participants