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_proto
8 : //
9 :
10 : #include <boost/http_proto/server/cors.hpp>
11 : #include <utility>
12 :
13 : namespace boost {
14 : namespace http_proto {
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& p)
29 0 : : p_(p)
30 : {
31 0 : }
32 :
33 0 : void set(field f, core::string_view s)
34 : {
35 0 : p_.res.set(f, s);
36 0 : }
37 :
38 0 : void append(field f, core::string_view v)
39 : {
40 0 : auto it = p_.res.find(f);
41 0 : if (it != p_.res.end())
42 : {
43 0 : std::string s = it->value;
44 0 : s += ", ";
45 0 : s += v;
46 0 : p_.res.set(it, s);
47 0 : }
48 : else
49 : {
50 0 : p_.res.set(f, v);
51 : }
52 0 : }
53 :
54 : private:
55 : route_params& p_;
56 : std::string v_;
57 : };
58 :
59 : } // (anon)
60 :
61 : // Access-Control-Allow-Origin
62 0 : static void setOrigin(
63 : Vary& v,
64 : route_params const&,
65 : cors_options const& options)
66 : {
67 0 : if( options.origin.empty() ||
68 0 : options.origin == "*")
69 : {
70 0 : v.set(field::access_control_allow_origin, "*");
71 0 : return;
72 : }
73 :
74 0 : v.set(
75 : field::access_control_allow_origin,
76 0 : options.origin);
77 0 : v.append(field::vary, to_string(field::origin));
78 : }
79 :
80 : // Access-Control-Allow-Methods
81 0 : static void setMethods(
82 : Vary& v,
83 : cors_options const& options)
84 : {
85 0 : if(! options.methods.empty())
86 : {
87 0 : v.set(
88 : field::access_control_allow_methods,
89 0 : options.methods);
90 0 : return;
91 : }
92 0 : v.set(
93 : field::access_control_allow_methods,
94 : "GET,HEAD,PUT,PATCH,POST,DELETE");
95 : }
96 :
97 : // Access-Control-Allow-Credentials
98 0 : static void setCredentials(
99 : Vary& v,
100 : cors_options const& options)
101 : {
102 0 : if(! options.credentials)
103 0 : return;
104 0 : v.set(
105 : field::access_control_allow_credentials,
106 : "true");
107 : }
108 :
109 : // Access-Control-Allowed-Headers
110 0 : static void setAllowedHeaders(
111 : Vary& v,
112 : route_params const& p,
113 : cors_options const& options)
114 : {
115 0 : if(! options.allowedHeaders.empty())
116 : {
117 0 : v.set(
118 : field::access_control_allow_headers,
119 0 : options.allowedHeaders);
120 0 : return;
121 : }
122 0 : auto s = p.res.value_or(
123 : field::access_control_request_headers, "");
124 0 : if(! s.empty())
125 : {
126 0 : v.set(field::access_control_allow_headers, s);
127 0 : v.append(field::vary, s);
128 : }
129 : }
130 :
131 : // Access-Control-Expose-Headers
132 0 : static void setExposeHeaders(
133 : Vary& v,
134 : cors_options const& options)
135 : {
136 0 : if(options.exposedHeaders.empty())
137 0 : return;
138 0 : v.set(
139 : field::access_control_expose_headers,
140 0 : options.exposedHeaders);
141 : }
142 :
143 : // Access-Control-Max-Age
144 0 : static void setMaxAge(
145 : Vary& v,
146 : cors_options const& options)
147 : {
148 0 : if(options.max_age.count() == 0)
149 0 : return;
150 0 : v.set(
151 : field::access_control_max_age,
152 0 : std::to_string(
153 : options.max_age.count()));
154 : }
155 :
156 : route_result
157 0 : cors::
158 : operator()(
159 : route_params& p) const
160 : {
161 0 : Vary v(p);
162 0 : if(p.req.method() ==
163 : method::options)
164 : {
165 : // preflight
166 0 : setOrigin(v, p, options_);
167 0 : setMethods(v, options_);
168 0 : setCredentials(v, options_);
169 0 : setAllowedHeaders(v, p, options_);
170 0 : setMaxAge(v, options_);
171 0 : setExposeHeaders(v, options_);
172 :
173 0 : if(options_.preFligthContinue)
174 0 : return route::next;
175 : // Safari and others need this for 204 or may hang
176 0 : p.res.set_status(options_.result);
177 0 : p.res.set_content_length(0);
178 0 : p.serializer.start(p.res);
179 0 : return route::send;
180 : }
181 : // actual response
182 0 : setOrigin(v, p, options_);
183 0 : setCredentials(v, options_);
184 0 : setExposeHeaders(v, options_);
185 0 : return route::next;
186 0 : }
187 :
188 : } // http_proto
189 : } // boost
|