Skip to content

Commit c59cdb7

Browse files
committed
[WIP] cors
1 parent 98cf872 commit c59cdb7

3 files changed

Lines changed: 245 additions & 0 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
#ifndef BOOST_HTTP_PROTO_SERVER_CORS_HPP
11+
#define BOOST_HTTP_PROTO_SERVER_CORS_HPP
12+
13+
#include <boost/http_proto/detail/config.hpp>
14+
#include <boost/http_proto/server/route_handler.hpp>
15+
#include <boost/http_proto/status.hpp>
16+
#include <chrono>
17+
18+
namespace boost {
19+
namespace http_proto {
20+
21+
struct cors_options
22+
{
23+
std::string origin;
24+
std::string methods;
25+
std::string exposedHeaders;
26+
std::chrono::seconds max_age{ 0 };
27+
status result = status::no_content;
28+
bool pre_flight_continue = false;
29+
bool credentials = false;
30+
};
31+
32+
class cors
33+
{
34+
public:
35+
BOOST_HTTP_PROTO_DECL
36+
explicit cors(
37+
cors_options options) noexcept;
38+
39+
BOOST_HTTP_PROTO_DECL
40+
route_result
41+
operator()(
42+
Request& req,
43+
Response& res) const;
44+
45+
private:
46+
cors_options options_;
47+
};
48+
49+
} // http_proto
50+
} // boost
51+
52+
#endif

src/server/cors.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
cors::
17+
cors(
18+
cors_options options) noexcept
19+
: options_(std::move(options))
20+
{
21+
// VFALCO TODO Validate the strings in options against RFC
22+
}
23+
24+
namespace {
25+
26+
struct Vary
27+
{
28+
Vary(Response& res)
29+
: res_(res)
30+
{
31+
}
32+
33+
void set(field f, core::string_view s)
34+
{
35+
res_.message.set(f, s);
36+
}
37+
38+
void append(field f, core::string_view v)
39+
{
40+
auto it = res_.message.find(f);
41+
if (it != res_.message.end())
42+
{
43+
std::string s = it->value;
44+
s += ", ";
45+
s += v;
46+
res_.message.set(it, s);
47+
}
48+
else
49+
{
50+
res_.message.set(f, v);
51+
}
52+
}
53+
54+
private:
55+
Response& res_;
56+
std::string v_;
57+
};
58+
59+
} // (anon)
60+
61+
// Access-Control-Allow-Origin
62+
static void setOrigin(
63+
Vary& v,
64+
Request const& req,
65+
cors_options const& options)
66+
{
67+
if( options.origin.empty() ||
68+
options.origin == "*")
69+
{
70+
v.set(field::access_control_allow_origin, "*");
71+
return;
72+
}
73+
74+
//auto const origin = req.message.value_or(field::origin, "");
75+
v.set(
76+
field::access_control_allow_origin,
77+
options.origin);
78+
v.append(field::vary, to_string(field::origin));
79+
}
80+
81+
// Access-Control-Allow-Credentials
82+
static void setCredentials(
83+
Vary& v,
84+
cors_options const& options)
85+
{
86+
if(! options.credentials)
87+
return;
88+
v.set(
89+
field::access_control_allow_credentials,
90+
"true");
91+
}
92+
93+
// Access-Control-Allow-Methods
94+
static void setMethods(
95+
Vary& v,
96+
cors_options const& options)
97+
{
98+
v.set(
99+
field::access_control_allow_methods,
100+
options.methods);
101+
}
102+
103+
// Access-Control-Allowed-Headers
104+
static void setAllowedHeaders(
105+
Vary& v,
106+
cors_options const& options)
107+
{
108+
}
109+
110+
// Access-Control-Max-Age
111+
static void setMaxAge(
112+
Vary& v,
113+
cors_options const& options)
114+
{
115+
if(options.max_age.count() == 0)
116+
return;
117+
v.set(
118+
field::access_control_max_age,
119+
std::to_string(
120+
options.max_age.count()));
121+
}
122+
123+
// Access-Control-Exposed-Headers
124+
static void setExposedHeaders(
125+
Vary& v,
126+
cors_options const& options)
127+
{
128+
}
129+
130+
route_result
131+
cors::
132+
operator()(
133+
Request& req,
134+
Response& res) const
135+
{
136+
Vary v(res);
137+
if(req.message.method() ==
138+
method::options)
139+
{
140+
// preflight
141+
setOrigin(v, req, options_);
142+
setCredentials(v, options_);
143+
setMethods(v, options_);
144+
setAllowedHeaders(v, options_);
145+
setMaxAge(v, options_);
146+
setExposedHeaders(v, options_);
147+
148+
if(options_.pre_flight_continue)
149+
return route::next;
150+
// Safari and others need this for 204 or may hang
151+
res.message.set_status(options_.result);
152+
res.message.set_content_length(0);
153+
return route::send;
154+
}
155+
// actual response
156+
setOrigin(v, options_);
157+
setCredentials(v, options_);
158+
setExposedHeaders(v, options_);
159+
return route::next;
160+
}
161+
162+
} // http_proto
163+
} // boost

test/unit/server/cors.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
// Test that header file is self-contained.
11+
#include <boost/http_proto/server/cors.hpp>
12+
13+
#include "test_suite.hpp"
14+
15+
namespace boost {
16+
namespace http_proto {
17+
18+
struct cors_test
19+
{
20+
void run()
21+
{
22+
}
23+
};
24+
25+
TEST_SUITE(
26+
cors_test,
27+
"boost.http_proto.server.cors");
28+
29+
} // http_proto
30+
} // boost

0 commit comments

Comments
 (0)