@@ -558,6 +558,141 @@ class DROGON_EXPORT HttpResponse
558558 return toResponse (std::forward<T>(obj));
559559 }
560560
561+ /* ! \brief Create an OPTIONS or CORS pre-flight response
562+ * \details If the request is not an OPTIONS request, returns a NULL
563+ * response\n
564+ * If it is a generic OPTIONS request, returns a 204 No Content
565+ * response with the Allow header\n
566+ * If it is a CORS pre-flight request, returns a 204 No Content
567+ * response with the CORS headers set
568+ *
569+ * Other status codes for CORS pre-flight answers:
570+ * - 400 Bad Request: if the request is malformed (missing
571+ * required headers)
572+ * - 403 Forbidden: if the Origin is not allowed + reason
573+ * in a X-Cors-Error header
574+ * - 403 Forbidden: if one of the headers in
575+ * Access-Control-Request-Headers is not allowed + reason in
576+ * a X-Cors-Error header
577+ * - 405 Method Not Allowed: if the requested method is
578+ * not allowed
579+ * \note CORS is a browser-side security mechanism.\n
580+ * Do not rely on Origin for authentication/authorization:
581+ * non-browser clients can spoof or omit it.\n
582+ * Enforce access control independently.
583+ * \param[in] request Drogon (OPTIONS) request
584+ * \param[in] allowedHeaders Set of allowed headers (for
585+ * Access-Control-Allow-Headers header)\n
586+ * (headers allowed by the controller path
587+ * handler)
588+ * \param[in] originValidator Function to validate the Origin header value
589+ * (allow the origin or not)\n
590+ * If allowCredentials is true, originValidator
591+ * _SHOULD_ enforce a strict allowlist
592+ * \param[in] allowNullOrigin Should be true to accept the "Origin: null"
593+ * header\n
594+ * (set for local file:// pages, sandboxed
595+ * iframes, opaque origins, data: URIs)
596+ * \param[in] allowCredentials Should be true to add the header
597+ * "Access-Control-Allow-Credentials: true"
598+ * (controls whether the browser may include
599+ * credentials such as cookies, HTTP auth, or
600+ * client certificates)\n
601+ * Note: Authorization (bearer) is not a
602+ * credential header; allow it via
603+ * allowedHeaders when needed
604+ * \param[in] allowPNA Should be true to accept the header
605+ * "Access-Control-Request-Private-Network"
606+ * (when a page from a less private address
607+ * space is trying to reach a more private
608+ * one, like internet -> intranet)\n
609+ * Note: specific to Chromium & derivatives
610+ * (Edge, Opera, Brave, ...), not in Firefox
611+ * or Safari
612+ * \param[in] maxAgeSeconds If set, adds the "Access-Control-Max-Age"
613+ * header with the given value (in seconds,
614+ * how long the results of a preflight
615+ * request can be cached by the navigator)
616+ * \returns the OPTIONS or CORS pre-flight response, or a null pointer if
617+ * the request is not an OPTIONS request
618+ */
619+ static HttpResponsePtr newOptionsResponse (
620+ const HttpRequestPtr &request,
621+ const std::function<bool (std::string_view)> &originValidator = nullptr,
622+ bool allowNullOrigin = false,
623+ bool allowCredentials = false,
624+ bool allowPNA = true,
625+ std::optional<unsigned int> maxAgeSeconds = {},
626+ const std::optional<std::set<std::string_view>> &allowedHeaders =
627+ std::nullopt );
628+
629+ /* ! \copydoc newOptionsResponse(const HttpRequestPtr&,
630+ * const std::function<bool(std::string_view)>&,
631+ * bool, bool, bool,
632+ * std::optional<unsigned int>,
633+ * const std::optional<std::set<std::string_view>>&)
634+ * \remarks Helper when specifying the allowed headers, when other
635+ * parameters may be default, to avoid having to specify them all
636+ */
637+ inline static HttpResponsePtr newOptionsResponse (
638+ const HttpRequestPtr &request,
639+ const std::set<std::string_view> &allowedHeaders,
640+ const std::function<bool (std::string_view)> &originValidator = nullptr,
641+ bool allowNullOrigin = false,
642+ bool allowCredentials = false,
643+ bool allowPNA = true,
644+ std::optional<unsigned int> maxAgeSeconds = {})
645+ {
646+ return newOptionsResponse (request,
647+ originValidator,
648+ allowNullOrigin,
649+ allowCredentials,
650+ allowPNA,
651+ maxAgeSeconds,
652+ allowedHeaders);
653+ }
654+
655+ /* ! \brief Add CORS headers to a response
656+ * \details Adds the CORS headers to a response for a normal request (a
657+ * CORS request but not a CORS preflight request):
658+ * - does nothing if it's an OPTIONS request, or
659+ * - if it's not a CORS request, or
660+ * - if it's a CORS preflight request
661+ * Else:
662+ * - adds Access-Control-Allow-Origin (if not yet present)
663+ * - adds Origin to the Vary header,
664+ * - sets or clears Access-Control-Allow-Credentials (if
665+ * allowCredentials is set)
666+ * - completes Access-Control-Expose-Headers
667+ * \param[in] request Drogon request (to get Origin)
668+ * \param[in] allowCredentials If set and true, adds the
669+ * "Access-Control-Allow-Credentials: true
670+ * header"\n
671+ * If set and false, removes the
672+ * "Access-Control-Allow-Credentials" header\n
673+ * If not set, leaves the
674+ * "Access-Control-Allow-Credentials" header
675+ * untouched\n
676+ * *MUST MATCH THE newOptionsResponse()
677+ * PRE-FLIGHT RESPONSE VALUE*
678+ * \param[in] exposedHeaders Set of exposed headers (for
679+ * Access-Control-Expose-Headers header)\n
680+ * These are the headers allowed to be exposed
681+ * to javascript by the remote browser\n
682+ * Note: they are *APPENDED* to any already
683+ * present in the response, they are not
684+ * REPLACED.\n
685+ * This allows to complete them in the
686+ * controller path handler.\n
687+ * If you want to REPLACE them, remove the
688+ * header before calling this function.
689+ * \note may be use both in the controller path handler and in a
690+ * pre-sending advice
691+ */
692+ void addCorsHeaders (const HttpRequestPtr &request,
693+ const std::set<std::string_view> &exposedHeaders = {},
694+ const std::optional<bool > &allowCredentials = {});
695+
561696 /* *
562697 * @brief If the response is a file response (i.e. created by
563698 * newFileResponse) returns the path on the filesystem. Otherwise a
0 commit comments