Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/http
8 : //
9 :
10 : #include <boost/http/server/cors.hpp>
11 : #include <utility>
12 :
13 : namespace boost {
14 : namespace http {
15 :
16 0 : cors::
17 : cors(
18 0 : cors_options options) noexcept
19 0 : : options_(std::move(options))
20 : {
21 : // VFALCO TODO Validate the strings in options against RFC
22 0 : }
23 :
24 : namespace {
25 :
26 : struct Vary
27 : {
28 0 : Vary(route_params& rp)
29 0 : : rp_(rp)
30 : {
31 0 : }
32 :
33 0 : void set(field f, core::string_view s)
34 : {
35 0 : rp_.res.set(f, s);
36 0 : }
37 :
38 0 : void append(field f, core::string_view v)
39 : {
40 0 : auto it = rp_.res.find(f);
41 0 : if(it != rp_.res.end())
42 : {
43 0 : std::string s = it->value;
44 0 : s += ", ";
45 0 : s += v;
46 0 : rp_.res.set(it, s);
47 0 : }
48 : else
49 : {
50 0 : rp_.res.set(f, v);
51 : }
52 0 : }
53 :
54 : private:
55 : route_params& rp_;
56 : };
57 :
58 : } // (anon)
59 :
60 : // Access-Control-Allow-Origin
61 0 : static void setOrigin(
62 : Vary& v,
63 : route_params const&,
64 : cors_options const& options)
65 : {
66 0 : if( options.origin.empty() ||
67 0 : options.origin == "*")
68 : {
69 0 : v.set(field::access_control_allow_origin, "*");
70 0 : return;
71 : }
72 :
73 0 : v.set(
74 : field::access_control_allow_origin,
75 0 : options.origin);
76 0 : v.append(field::vary, to_string(field::origin));
77 : }
78 :
79 : // Access-Control-Allow-Methods
80 0 : static void setMethods(
81 : Vary& v,
82 : cors_options const& options)
83 : {
84 0 : if(! options.methods.empty())
85 : {
86 0 : v.set(
87 : field::access_control_allow_methods,
88 0 : options.methods);
89 0 : return;
90 : }
91 0 : v.set(
92 : field::access_control_allow_methods,
93 : "GET,HEAD,PUT,PATCH,POST,DELETE");
94 : }
95 :
96 : // Access-Control-Allow-Credentials
97 0 : static void setCredentials(
98 : Vary& v,
99 : cors_options const& options)
100 : {
101 0 : if(! options.credentials)
102 0 : return;
103 0 : v.set(
104 : field::access_control_allow_credentials,
105 : "true");
106 : }
107 :
108 : // Access-Control-Allowed-Headers
109 0 : static void setAllowedHeaders(
110 : Vary& v,
111 : route_params const& rp,
112 : cors_options const& options)
113 : {
114 0 : if(! options.allowedHeaders.empty())
115 : {
116 0 : v.set(
117 : field::access_control_allow_headers,
118 0 : options.allowedHeaders);
119 0 : return;
120 : }
121 0 : auto s = rp.req.value_or(
122 : field::access_control_request_headers, "");
123 0 : if(! s.empty())
124 : {
125 0 : v.set(field::access_control_allow_headers, s);
126 0 : v.append(field::vary, s);
127 : }
128 : }
129 :
130 : // Access-Control-Expose-Headers
131 0 : static void setExposeHeaders(
132 : Vary& v,
133 : cors_options const& options)
134 : {
135 0 : if(options.exposedHeaders.empty())
136 0 : return;
137 0 : v.set(
138 : field::access_control_expose_headers,
139 0 : options.exposedHeaders);
140 : }
141 :
142 : // Access-Control-Max-Age
143 0 : static void setMaxAge(
144 : Vary& v,
145 : cors_options const& options)
146 : {
147 0 : if(options.max_age.count() == 0)
148 0 : return;
149 0 : v.set(
150 : field::access_control_max_age,
151 0 : std::to_string(
152 : options.max_age.count()));
153 : }
154 :
155 : route_task
156 0 : cors::
157 : operator()(
158 : route_params& rp) const
159 : {
160 : Vary v(rp);
161 : if(rp.req.method() == method::options)
162 : {
163 : // preflight
164 : setOrigin(v, rp, options_);
165 : setMethods(v, options_);
166 : setCredentials(v, options_);
167 : setAllowedHeaders(v, rp, options_);
168 : setMaxAge(v, options_);
169 : setExposeHeaders(v, options_);
170 :
171 : if(options_.preFlightContinue)
172 : co_return route::next;
173 :
174 : // Safari and others need this for 204 or may hang
175 : rp.res.set_status(options_.result);
176 : co_return co_await rp.send("");
177 : }
178 :
179 : // actual response
180 : setOrigin(v, rp, options_);
181 : setCredentials(v, options_);
182 : setExposeHeaders(v, options_);
183 : co_return route::next;
184 0 : }
185 :
186 : } // http
187 : } // boost
|