Skip to content

feat(martin-server): add support for basic CORS configuration #1815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 30 commits into from
May 28, 2025

Conversation

NINNiT
Copy link
Contributor

@NINNiT NINNiT commented May 1, 2025

As mentioned in #931, I've tried to come up with a basic implementation/spec which allows configuration of CORS..

Before:
Static CORS configuration in server.rs.

let cors_middleware = Cors::default()
         .allow_any_origin()
         .allowed_methods(vec!["GET"]);

This translates to the following logic:

  • If an incoming request has an Origin Header, the value of this Headers is used to set Access-Control-Allow-Origin (not *)
  • If an incoming request has no Origin Header, Access-Control-Allow-Origin is not set, however vary headers are still being sent

After:

  • Same default behavior as before (CORS enabled, all origins allowed, only GET methods allowed). There should be no breaking changes for existing deployments.
  • Allows configuration of the following properties:
# CORS Configuration
# Can also be disabled via `cors: false`
cors:
  # Sets the `Access-Control-Allow-Origin` header [default: *]
  # '*' will use the requests `ORIGIN` header
  origin:
    - https://example.org
  # Sets `Access-Control-Max-Age` Header. [default: null]
  # null means not setting the header for preflight requests
  max_age: 3600
  • Conditionally wraps the actix app in the CORS middleware, if the latter is enabled
   app.wrap(Condition::new(
            cors_middleware.is_some(),
            cors_middleware.unwrap_or_default(),
        ))
  • Adds tests for CORS

  • Config is nested under SrvConfig . I think this is fitting, as it's not a source.

Let me know if you have any suggestions, changes that should be applied or if you'd like to take this over. 🦀
@CommanderStorm im gonna ping you here, hope that's okay

@Copilot Copilot AI review requested due to automatic review settings May 1, 2025 11:55
@NINNiT NINNiT marked this pull request as draft May 1, 2025 11:55
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces configurable CORS support to the Martin server while preserving existing default behavior. Key changes include:

  • Adding a new CorsConfig and its make_cors_middleware method.
  • Updating server.rs to conditionally apply CORS middleware based on configuration.
  • Incorporating new tests and updating configuration documentation.

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
martin/tests/cors_test.rs Added tests to validate the new CORS configuration behavior.
martin/src/srv/server.rs Updated middleware setup to wrap the app conditionally with the CORS middleware.
martin/src/srv/mod.rs Imported the new CORS module.
martin/src/srv/cors.rs Introduced CorsConfig struct with middleware creation logic for CORS handling.
martin/src/srv/config.rs Integrated CorsConfig into SrvConfig and added a YAML parsing test for CORS settings.
docs/src/config-file.md Updated the configuration documentation to include CORS setup examples.
Comments suppressed due to low confidence (2)

docs/src/config-file.md:53

  • The configuration documentation uses 'allowed_origins' while the code uses 'origin'. Consider aligning these names for consistency.
allowed_origins:

martin/src/srv/cors.rs:8

  • Consider renaming the field 'origin' to 'allowed_origins' to match the configuration documentation and improve clarity.
pub origin: Vec<String>,

@NINNiT NINNiT requested a review from Copilot May 1, 2025 11:57
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

The PR adds configurable CORS support to the martin-server, ensuring backward compatibility while allowing deployment-specific CORS configuration via the server config and YAML file. It also includes updated tests and documentation for the new CORS feature.

  • Introduces a new CorsConfig with middleware generation in its own module.
  • Updates the server initialization to conditionally wrap the app with CORS middleware based on configuration.
  • Adds relevant tests and documentation for the configurable CORS settings.

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
martin/tests/cors_test.rs Adds tests to verify CORS behavior under various configurations.
martin/src/srv/server.rs Updates server creation to use the new configurable CORS middleware.
martin/src/srv/mod.rs Registers the new CORS module.
martin/src/srv/cors.rs Implements CorsConfig and middleware generation logic.
martin/src/srv/config.rs Updates SrvConfig to include CorsConfig and tests YAML parsing.
docs/src/config-file.md Enhances documentation with new CORS configuration options.
Comments suppressed due to low confidence (1)

martin/src/srv/server.rs:187

  • [nitpick] Consider caching the result of cors_middleware.unwrap_or_default() in a variable to avoid calling it twice, which can improve readability and reduce the chance of future errors if the unwrap logic evolves.
app.wrap(Condition::new(cors_middleware.is_some(), cors_middleware.unwrap_or_default(),))

@NINNiT NINNiT requested a review from Copilot May 1, 2025 12:10
@NINNiT NINNiT marked this pull request as ready for review May 1, 2025 12:10
Copilot

This comment was marked as resolved.

@NINNiT NINNiT marked this pull request as draft May 1, 2025 12:11
renovate-changelog-fetcher and others added 2 commits May 2, 2025 23:27
adds a few more tests (e.g for CORS preflight requests -> max-age)
@nyurik nyurik added config Relates to Martin configuration serving Related to web serving component breaking and removed breaking labels May 2, 2025
@@ -44,6 +44,17 @@ preferred_encoding: gzip
# Enable or disable Martin web UI. At the moment, only allows `enable-for-all` which enables the web UI for all connections. This may be undesirable in a production environment. [default: disable]
web_ui: disable

# CORS Configuration
# Can also be disabled via `cors: false`
Copy link
Member

Choose a reason for hiding this comment

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

need to document what does true means, and what happens if not set at all (default). Note that default here and in sub-items is a bit confusing, i.e. does cors: {} equal to cors: ~ ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point - i'll expand on that and double check what happens. might even add some tests
never seen ~ or {} in the wild though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right now, both cors: {} and cors: ~ seem to enable CORS using our defaults.

Copy link
Contributor Author

@NINNiT NINNiT May 2, 2025

Choose a reason for hiding this comment

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

As it stands right now, it'll always fall back to the defaults.

  • no cors option at all -> Enable using defaults
  • cors: true -> Enable using defaults
  • only configuring a subset and omitting certain properties-> use defaults for properties that are not explicitly set.

this...

cors:
  max_age: 3600

would translate to this:

cors:
  origin: ["*"]
  max_age: 3600

The only way to disable CORS right now, is to explicitly set cors: false.

I guess cors: ~ should also disable it? What would you prefer

Copy link
Member

Choose a reason for hiding this comment

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

No, null is the same as skipped, so it should be the default

@NINNiT
Copy link
Contributor Author

NINNiT commented May 6, 2025

I'm wondering if we actually want to fall back to defaults when CORS is explicitly configured.
For example, when only a subset of properties is specified (e.g., just max_age), should we implicitly apply defaults to the omitted properties?

This:

cors:
  max_age: 3600

would currently be interpreted as:

cors:
  origin: ["*"] # default value
  max_age: 3600

Is that the intended behavior, or should partial configuration require more explicit values to avoid unintended/hidden defaults?

#[test]
fn test_cors_middleware_disabled() {
let config = CorsConfig::SimpleFlag(false);
assert_eq!(config.make_cors_middleware(), Ok(None));
Copy link
Contributor Author

@NINNiT NINNiT May 27, 2025

Choose a reason for hiding this comment

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

@CommanderStorm
Some of your test changes aren't compiling on my end (stable toolchain, v1.87.0)

error[E0369]: binary operation `==` cannot be applied to type `std::result::Result<std::option::Option<Cors>, utils::error::MartinError>`
   --> martin/src/srv/cors.rs:121:9
    |
121 |         assert_eq!(config.make_cors_middleware(), Ok(None));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |         |
    |         std::result::Result<std::option::Option<Cors>, utils::error::MartinError>
    |         std::result::Result<std::option::Option<Cors>, utils::error::MartinError>
    
    note: an implementation of `PartialEq` might be missing for `utils::error::MartinError`
   --> martin/src/utils/error.rs:30:1
    |
30  | pub enum MartinError {
    | ^^^^^^^^^^^^^^^^^^^^ must implement `PartialEq`

Looks like we'll have to implement PartialEq for MartinError
or assert Ok and Option separately (the latter after unwrapping)

@NINNiT NINNiT marked this pull request as ready for review May 27, 2025 20:03
Copy link
Collaborator

@CommanderStorm CommanderStorm left a comment

Choose a reason for hiding this comment

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

Good Job.
I have included a few nits and will finish up the rest when I have more stable Internet.

Sorry for the long march this feature has been and thanks for completing it.
We really appreciate the effort ♥️

Comment on lines 168 to 169
.make_cors_middleware()
.expect("CORS configuration is invalid");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I need to check if logs are printed.
Might need to move this up a bit.

@CommanderStorm CommanderStorm enabled auto-merge (squash) May 27, 2025 20:43
@CommanderStorm CommanderStorm disabled auto-merge May 27, 2025 20:43
@NINNiT
Copy link
Contributor Author

NINNiT commented May 27, 2025

If we are through with all the changes, I'd like to test this one last time in the real world.
Just to be sure, before this makes it into the release 😆

@CommanderStorm
Copy link
Collaborator

I need to check if logs are printed.
Might need to move this up a bit.

I also need to check this part ^^

@CommanderStorm CommanderStorm requested review from nyurik and Copilot May 27, 2025 23:09
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request introduces configurable CORS support for the Martin server while preserving default behavior for existing deployments. Key changes include:

  • Adding a new CORS middleware module (martin/src/srv/cors.rs) that builds the actix_cors middleware based on configuration.
  • Updating the server initialization to validate and conditionally apply the CORS middleware.
  • Expanding tests and documentation to cover various CORS configurations.

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
martin/tests/cors_test.rs New tests added to validate different CORS configuration scenarios.
martin/src/utils/error.rs Introduces a new CorsError variant for error handling in CORS logic.
martin/src/srv/server.rs Updates server setup to integrate and validate CORS middleware.
martin/src/srv/mod.rs Exposes the new cors module to the rest of the application.
martin/src/srv/cors.rs New module implementing the CORS configuration and middleware creation.
martin/src/srv/config.rs Extends server configuration to include CORS settings.
docs/src/config-file.md Documents the new CORS configuration options.
.dockerignore Updates ignore patterns by excluding .aider* files.

@nyurik
Copy link
Member

nyurik commented May 28, 2025

Thank you!!!

@nyurik nyurik merged commit 166ebcb into maplibre:main May 28, 2025
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
config Relates to Martin configuration serving Related to web serving component
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants