Skip to content

Commit 8d640ba

Browse files
Add cors example to demonstrate cross-origin support in drogon (#2323)
1 parent f6b5404 commit 8d640ba

File tree

3 files changed

+158
-1
lines changed

3 files changed

+158
-1
lines changed

examples/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ add_executable(redis_chat redis_chat/main.cc
3434
add_executable(async_stream async_stream/main.cc
3535
async_stream/RequestStreamExampleCtrl.cc)
3636

37+
add_executable(cors cors/main.cc)
38+
3739
set(example_targets
3840
benchmark
3941
client
@@ -45,7 +47,8 @@ set(example_targets
4547
jsonstore
4648
redis_simple
4749
redis_chat
48-
async_stream)
50+
async_stream
51+
cors)
4952

5053
# Add warnings for our example targets--some warnings (such as -Wunused-parameter) only appear
5154
# when the templated functions are instantiated at their point of use.

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ proxy with a simple round robin
1616
11. [redis_cache](https://github.com/drogonframework/drogon/tree/master/examples/redis_cache) - An example for using coroutines of Redis clients
1717
12. [redis_chat](https://github.com/drogonframework/drogon/tree/master/examples/redis_chat) - A chatroom server built with websocket and Redis pub/sub service
1818
13. [prometheus_example](https://github.com/drogonframework/drogon/tree/master/examples/prometheus_example) - An example of how to use the Prometheus exporter in Drogon
19+
14. [cors](https://github.com/drogonframework/drogon/tree/master/examples/cors) - An example demonstrating how to implement CORS (Cross-Origin Resource Sharing) support in Drogon
1920

2021
### [TechEmpower Framework Benchmarks](https://github.com/TechEmpower/FrameworkBenchmarks) test suite
2122

examples/cors/main.cc

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#include <drogon/HttpAppFramework.h>
2+
#include <drogon/HttpResponse.h>
3+
#include <drogon/drogon.h>
4+
#include "trantor/utils/Logger.h"
5+
6+
using namespace drogon;
7+
8+
/// Configure Cross-Origin Resource Sharing (CORS) support.
9+
///
10+
/// This function registers both synchronous pre-processing advice for handling
11+
/// OPTIONS preflight requests and post-handling advice to inject CORS headers
12+
/// into all responses dynamically based on the incoming request headers.
13+
void setupCors()
14+
{
15+
// Register sync advice to handle CORS preflight (OPTIONS) requests
16+
drogon::app().registerSyncAdvice([](const drogon::HttpRequestPtr &req)
17+
-> drogon::HttpResponsePtr {
18+
if (req->method() == drogon::HttpMethod::Options)
19+
{
20+
auto resp = drogon::HttpResponse::newHttpResponse();
21+
22+
// Set Access-Control-Allow-Origin header based on the Origin
23+
// request header
24+
const auto &origin = req->getHeader("Origin");
25+
if (!origin.empty())
26+
{
27+
resp->addHeader("Access-Control-Allow-Origin", origin);
28+
}
29+
30+
// Set Access-Control-Allow-Methods based on the requested method
31+
const auto &requestMethod =
32+
req->getHeader("Access-Control-Request-Method");
33+
if (!requestMethod.empty())
34+
{
35+
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
36+
}
37+
38+
// Allow credentials to be included in cross-origin requests
39+
resp->addHeader("Access-Control-Allow-Credentials", "true");
40+
41+
// Set allowed headers from the Access-Control-Request-Headers
42+
// header
43+
const auto &requestHeaders =
44+
req->getHeader("Access-Control-Request-Headers");
45+
if (!requestHeaders.empty())
46+
{
47+
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
48+
}
49+
50+
return std::move(resp);
51+
}
52+
return {};
53+
});
54+
55+
// Register post-handling advice to add CORS headers to all responses
56+
drogon::app().registerPostHandlingAdvice(
57+
[](const drogon::HttpRequestPtr &req,
58+
const drogon::HttpResponsePtr &resp) -> void {
59+
// Set Access-Control-Allow-Origin based on the Origin request
60+
// header
61+
const auto &origin = req->getHeader("Origin");
62+
if (!origin.empty())
63+
{
64+
resp->addHeader("Access-Control-Allow-Origin", origin);
65+
}
66+
67+
// Reflect the requested Access-Control-Request-Method back in the
68+
// response
69+
const auto &requestMethod =
70+
req->getHeader("Access-Control-Request-Method");
71+
if (!requestMethod.empty())
72+
{
73+
resp->addHeader("Access-Control-Allow-Methods", requestMethod);
74+
}
75+
76+
// Allow credentials to be included in cross-origin requests
77+
resp->addHeader("Access-Control-Allow-Credentials", "true");
78+
79+
// Reflect the requested Access-Control-Request-Headers back
80+
const auto &requestHeaders =
81+
req->getHeader("Access-Control-Request-Headers");
82+
if (!requestHeaders.empty())
83+
{
84+
resp->addHeader("Access-Control-Allow-Headers", requestHeaders);
85+
}
86+
});
87+
}
88+
89+
/**
90+
* Main function to start the Drogon application with CORS-enabled routes.
91+
* This example includes:
92+
* - A simple GET endpoint `/hello` that returns a greeting message.
93+
* - A POST endpoint `/echo` that echoes back the request body.
94+
* You can test with curl to test the CORS support:
95+
*
96+
```
97+
curl -i -X OPTIONS http://localhost:8000/echo \
98+
-H "Origin: http://localhost:3000" \
99+
-H "Access-Control-Request-Method: POST" \
100+
-H "Access-Control-Request-Headers: Content-Type"
101+
```
102+
or
103+
```
104+
curl -i -X POST http://localhost:8000/echo \
105+
-H "Origin: http://localhost:3000" \
106+
-H "Content-Type: application/json" \
107+
-d '{"key":"value"}'
108+
```
109+
*/
110+
int main()
111+
{
112+
// Listen on port 8000 for all interfaces
113+
app().addListener("0.0.0.0", 8000);
114+
115+
// Setup CORS support
116+
setupCors();
117+
118+
// Register /hello route for GET and OPTIONS methods
119+
app().registerHandler(
120+
"/hello",
121+
[](const HttpRequestPtr &req,
122+
std::function<void(const HttpResponsePtr &)> &&callback) {
123+
auto resp = HttpResponse::newHttpResponse();
124+
resp->setBody("Hello from Drogon!");
125+
126+
// Log client IP address
127+
LOG_INFO << "Request to /hello from " << req->getPeerAddr().toIp();
128+
129+
callback(resp);
130+
},
131+
{Get, Options});
132+
133+
// Register /echo route for POST and OPTIONS methods
134+
app().registerHandler(
135+
"/echo",
136+
[](const HttpRequestPtr &req,
137+
std::function<void(const HttpResponsePtr &)> &&callback) {
138+
auto resp = HttpResponse::newHttpResponse();
139+
resp->setBody(std::string("Echo: ").append(req->getBody()));
140+
141+
// Log client IP and request body
142+
LOG_INFO << "Request to /echo from " << req->getPeerAddr().toIp();
143+
LOG_INFO << "Echo content: " << req->getBody();
144+
145+
callback(resp);
146+
},
147+
{Post, Options});
148+
149+
// Start the application main loop
150+
app().run();
151+
152+
return 0;
153+
}

0 commit comments

Comments
 (0)