Skip to content

Conversation

@Greisby
Copy link
Contributor

@Greisby Greisby commented Jan 21, 2026

Summary

This PR adds an opt-out mechanism for Drogon’s built-in handling of OPTIONS (CORS preflight) requests, enabling applications to implement custom preflight logic in their own route handlers when needed.

Concretely:

  • It introduces a small helper middleware (HttpOptionsMiddleware) that tags OPTIONS requests with a request attribute (drogon.customCORShandling).
  • HttpServer checks this attribute before performing its internal OPTIONS short-circuit, and skips the internal preflight response when custom handling is requested.
  • In this case, it adds all the methods reported by the HttpControllersRouter to the request attributes so that the path handlers known all the registered methods.

Motivation / Why this is useful

Drogon’s default shortcut responds with permissive CORS headers based on the request headers.
This is convenient for many APIs, but it can be limiting when an application needs a non-default CORS policy that depends on runtime context or route-specific rules.

Typical cases include:

  • Restricting allowed origins dynamically (e.g., per tenant, per resource, per environment)
  • Enforcing per-route or per-origin restrictions on allowed headers
  • Customizing Access-Control-Max-Age (or other preflight response headers) based on endpoint semantics
  • Implementing strict preflight rejection (explicitly returning an error when a requested header or method is not allowed)
  • Integrating preflight decisions with application authorization logic

Today, the internal OPTIONS shortcut prevents those applications from routing preflight to handlers in a straightforward way.
This PR provides a minimal and explicit opt-out, without changing default behavior.

Design

  • Default behavior remains unchanged: if drogon.customCORShandling is absent/false, Drogon continues to handle OPTIONS internally as before.
  • When the request attribute is present and true, the server does not short-circuit OPTIONS, allowing the request to reach the application’s handler logic.
  • A helper middleware is provided to set this attribute:
    • Can be registered manually (singleton instance)
    • And used via route registration macros for endpoints that need custom behavior

Backward compatibility

  • No behavior change for existing applications unless they use this middleware to explicitly set the drogon.customCORShandling and drogon.corsMethods attributes.
  • The server-side check is gated on the drogon.customCORShandling attribute, so performance impact is negligible.

Notes

The current implementation keeps the change minimal.

Possible follow-up (not part of this PR)

Provide small helper utilities to build consistent OPTIONS / CORS preflight responses (e.g. isCorsPreflightRequest(), createOptionsResponse()), so applications can implement custom policies more easily.
II’m keeping this PR focused on the opt-out mechanism to minimize API surface and review scope.

@Greisby Greisby requested a review from Copilot January 21, 2026 13:58
@Greisby Greisby self-assigned this Jan 21, 2026
@Greisby Greisby requested a review from hwc0919 January 21, 2026 14:04
@Greisby Greisby requested a review from an-tao January 21, 2026 14:07
Copy link

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 an opt-out mechanism for Drogon's built-in OPTIONS/CORS preflight request handling, allowing applications to implement custom CORS policies through their own route handlers. The default behavior remains unchanged, ensuring full backward compatibility.

Changes:

  • Added conditional logic in HttpServer to skip internal OPTIONS handling when a custom flag is set
  • Introduced HttpOptionsMiddleware classes that tag OPTIONS requests with a customCORShandling attribute
  • Provided comprehensive documentation for the new middleware usage patterns

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/src/HttpServer.cc Adds conditional check to skip internal OPTIONS handling when customCORShandling attribute is true
lib/inc/drogon/HttpMiddleware.h Introduces HttpOptionsMiddleware template classes and implementations for flagging OPTIONS requests

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Greisby Greisby marked this pull request as ready for review January 21, 2026 14:41
@Greisby Greisby marked this pull request as draft January 22, 2026 07:05
@Greisby
Copy link
Contributor Author

Greisby commented Jan 22, 2026

I just realized that the custom Options handler needs to know the registered methods for the path of the request, so I also added them as attribute when custom handling is requested

Copy link

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

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


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

* 1. globally via
* app().registerMiddleware(std::make_shared<drogon::HttpOptionsMiddleware>())
* 2. on every path handlers that need non-default handling, with
* ADD_METHOD_TO(... , "drogon::HttpOptionsMiddleware")
Copy link

Copilot AI Jan 22, 2026

Choose a reason for hiding this comment

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

The documentation example is incorrect. When using ADD_METHOD_TO with a string reference (line 164), the middleware must have AutoCreation set to true so the framework can automatically instantiate it. However, HttpOptionsMiddleware has AutoCreation set to false (line 189).

The example should use "drogon::HttpOptionsMiddlewareAuto" instead, which has AutoCreation set to true (line 183-184). Alternatively, document that only option 1 (manual registration via registerMiddleware) is supported.

Suggested change
* ADD_METHOD_TO(... , "drogon::HttpOptionsMiddleware")
* ADD_METHOD_TO(... , "drogon::HttpOptionsMiddlewareAuto")

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's not what I understood (and use).
I always register middleware with auto-creation set to false, and then use their name in paths registration.
Is this the wrong way?

@an-tao
Copy link
Member

an-tao commented Jan 22, 2026

@Greisby Thanks so much for your patch. Is it more lightweight and efficient if we use configuration items instead of middleware to achieve this effect?

@Greisby
Copy link
Contributor Author

Greisby commented Jan 22, 2026

@an-tao
Hi!
What do you mean with configuration items?
Is there another mechanism I'm not aware of?

For info, the use would be similar to this:

// default handling
ADD_METHOD_TO(Controller::testGet, "/test1/{1}", drogon::Options, drogon::Get);
ADD_METHOD_TO(Controller::testPut, "/test1/{1}", drogon::Put);
// custom handling
ADD_METHOD_TO(Controller::testOptions, "/test2/{1}", drogon::Options, "drogon::HttpOptionsMiddleware");
ADD_METHOD_TO(Controller::testGet, "/test2/{1}", drogon::Get);
ADD_METHOD_TO(Controller::testPut, "/test2/{1}", drogon::Put);

void Controller::testOptions(...) {
    auto response = drogon::createOptionsResponse(request, { "Authorization" }, originValidator, CORS_MAX_AGE);
    callback(response);
}

@Greisby Greisby marked this pull request as ready for review January 22, 2026 15:52
@an-tao
Copy link
Member

an-tao commented Jan 23, 2026

@an-tao Hi! What do you mean with configuration items? Is there another mechanism I'm not aware of?

For info, the use would be similar to this:

// default handling
ADD_METHOD_TO(Controller::testGet, "/test1/{1}", drogon::Options, drogon::Get);
ADD_METHOD_TO(Controller::testPut, "/test1/{1}", drogon::Put);
// custom handling
ADD_METHOD_TO(Controller::testOptions, "/test2/{1}", drogon::Options, "drogon::HttpOptionsMiddleware");
ADD_METHOD_TO(Controller::testGet, "/test2/{1}", drogon::Get);
ADD_METHOD_TO(Controller::testPut, "/test2/{1}", drogon::Put);

void Controller::testOptions(...) {
    auto response = drogon::createOptionsResponse(request, { "Authorization" }, originValidator, CORS_MAX_AGE);
    callback(response);
}

You are right, the middleware solution is more flexible, if we use an option in the configuration file, it changes global behavior of all option requests.

@Greisby
Copy link
Contributor Author

Greisby commented Jan 23, 2026

Ah ok, I understand now. You meant an cmake configuration option.
That would be problematic when multiple projects share the same libraries (like a shared vcpkg), but not all have the same needs.

I think that the extra test in HttpServer::requestPreHandling won't impact the performance, and it remains flexible as it is now.

@an-tao
Copy link
Member

an-tao commented Jan 23, 2026

Ah ok, I understand now. You meant an cmake configuration option. That would be problematic when multiple projects share the same libraries (like a shared vcpkg), but not all have the same needs.

I think that the extra test in HttpServer::requestPreHandling won't impact the performance, and it remains flexible as it is now.

I mean an option in the config.json file that affects the entire application.

@Greisby
Copy link
Contributor Author

Greisby commented Jan 23, 2026

Ok, got it.
Personally I never used it. I have my own config to restrict what can be tweaked, and I do the API calls.

@Greisby Greisby merged commit 3495522 into drogonframework:master Jan 26, 2026
62 of 63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants