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(Response& res)
29 0 : : res_(res)
30 : {
31 0 : }
32 :
33 0 : void set(field f, core::string_view s)
34 : {
35 0 : res_.message.set(f, s);
36 0 : }
37 :
38 0 : void append(field f, core::string_view v)
39 : {
40 0 : auto it = res_.message.find(f);
41 0 : if (it != res_.message.end())
42 : {
43 0 : std::string s = it->value;
44 0 : s += ", ";
45 0 : s += v;
46 0 : res_.message.set(it, s);
47 0 : }
48 : else
49 : {
50 0 : res_.message.set(f, v);
51 : }
52 0 : }
53 :
54 : private:
55 : Response& res_;
56 : std::string v_;
57 : };
58 :
59 : } // (anon)
60 :
61 : // Access-Control-Allow-Origin
62 0 : static void setOrigin(
63 : Vary& v,
64 : Request 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 : Request const& req,
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 = req.message.value_or(
123 : field::access_control_request_headers, "");
124 0 : if(! s.empty())
125 : {
126 0 : v.set(
127 : field::access_control_allow_headers,
128 : s);
129 0 : v.append(field::vary, s);
130 : }
131 : }
132 :
133 : // Access-Control-Expose-Headers
134 0 : static void setExposeHeaders(
135 : Vary& v,
136 : cors_options const& options)
137 : {
138 0 : if(options.exposedHeaders.empty())
139 0 : return;
140 0 : v.set(
141 : field::access_control_expose_headers,
142 0 : options.exposedHeaders);
143 : }
144 :
145 : // Access-Control-Max-Age
146 0 : static void setMaxAge(
147 : Vary& v,
148 : cors_options const& options)
149 : {
150 0 : if(options.max_age.count() == 0)
151 0 : return;
152 0 : v.set(
153 : field::access_control_max_age,
154 0 : std::to_string(
155 : options.max_age.count()));
156 : }
157 :
158 : route_result
159 0 : cors::
160 : operator()(
161 : Request& req,
162 : Response& res) const
163 : {
164 0 : Vary v(res);
165 0 : if(req.message.method() ==
166 : method::options)
167 : {
168 : // preflight
169 0 : setOrigin(v, req, options_);
170 0 : setMethods(v, options_);
171 0 : setCredentials(v, options_);
172 0 : setAllowedHeaders(v, req, options_);
173 0 : setMaxAge(v, options_);
174 0 : setExposeHeaders(v, options_);
175 :
176 0 : if(options_.preFligthContinue)
177 0 : return route::next;
178 : // Safari and others need this for 204 or may hang
179 0 : res.message.set_status(options_.result);
180 0 : res.message.set_content_length(0);
181 0 : res.serializer.start(res.message);
182 0 : return route::send;
183 : }
184 : // actual response
185 0 : setOrigin(v, req, options_);
186 0 : setCredentials(v, options_);
187 0 : setExposeHeaders(v, options_);
188 0 : return route::next;
189 0 : }
190 :
191 : } // http_proto
192 : } // boost
|