SNode.C
Loading...
Searching...
No Matches
express_compat_server.cpp File Reference
#include "core/SNodeC.h"
#include "express/legacy/in/WebApp.h"
#include "log/Logger.h"
#include <nlohmann/json.hpp>
#include <string>
Include dependency graph for express_compat_server.cpp:

Go to the source code of this file.

Classes

class  Router

Typedefs

using Trace = std::vector<json>

Functions

static json snapshot (const std::shared_ptr< express::Request > &req, const std::string &label)
static void ensureTrace (const std::shared_ptr< express::Request > &req)
static void tracePush (const std::shared_ptr< express::Request > &req, const std::string &label)
static json traceGet (const std::shared_ptr< express::Request > &req)
int main (int argc, char *argv[])

Typedef Documentation

◆ Trace

typedef std::vector< json > Trace = std::vector<json>

Definition at line 19 of file express_compat_server.cpp.

Function Documentation

◆ ensureTrace()

void ensureTrace ( const std::shared_ptr< express::Request > & req)
static

Definition at line 49 of file express_compat_server.cpp.

49 {
50 // Create the trace attribute if it doesn't exist yet.
51 req->setAttribute<Trace>(Trace{}, "trace");
52}
constexpr bool setAttribute(const Attribute &attribute, const std::string &subKey="", bool overwrite=false)
std::vector< json > Trace

References utils::MultibleAttributeInjector::setAttribute().

Referenced by tracePush().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int main ( int argc,
char * argv[] )

Definition at line 75 of file express_compat_server.cpp.

75 {
76 core::SNodeC::init(argc, argv);
77
78 const express::legacy::in::WebApp app("express-compat");
79
80 // Meta
81 app.get("/__meta", [] APPLICATION(req, res) {
82 res->json({{"ok", true}, {"server", "snodec"}, {"express", true}});
83 });
84
85 // Basic
86 app.get("/health", [] APPLICATION(req, res) {
87 res->json({{"ok", true}, {"label", "health"}});
88 });
89
90 // Case-insensitive routing demo
91 app.get("/Case/Path", [] APPLICATION(req, res) {
92 res->json(snapshot(req, "case"));
93 });
94
95 // Trailing slash demo (strictRouting off => matches both)
96 app.get("/trail/", [] APPLICATION(req, res) {
97 res->json(snapshot(req, "trail"));
98 });
99
100 // Wildcards
101 app.get("/file/*", [] APPLICATION(req, res) {
102 res->json(snapshot(req, "file"));
103 });
104 app.get("/a/*/b/*", [] APPLICATION(req, res) {
105 res->json(snapshot(req, "multi_wild"));
106 });
107
108 // Param decoding
109 app.get("/p/:x", [] APPLICATION(req, res) {
110 res->json(snapshot(req, "param"));
111 });
112
113 // Query decoding/casing
114 app.get("/query/echo", [] APPLICATION(req, res) {
115 res->json(snapshot(req, "query_echo"));
116 });
117
118 // HEAD -> GET fallback (handled by dispatcher/response code)
119 app.get("/head-demo", [] APPLICATION(req, res) {
120 res->set("X-Demo", "1");
121 res->send("head body");
122 });
123
124 // next('route') demo
125 app.get(
126 "/nr/:id(\\d+)",
127 [] MIDDLEWARE(req, res, next) {
128 if (req->params["id"] == "0") {
129 next("route");
130 return;
131 }
132 next();
133 },
134 [] APPLICATION(req, res) {
135 res->json(snapshot(req, "nr_primary"));
136 });
137 app.get("/nr/:id(\\d+)", [] APPLICATION(req, res) {
138 res->json(snapshot(req, "nr_fallback"));
139 });
140
141 // next('router') demo
142 const Router guarded;
143 guarded.use([] MIDDLEWARE(req, res, next) {
144 if (req->queries["allow"] != "true") {
145 next("router");
146 return;
147 }
148 next();
149 });
150 guarded.get("/stats", [] APPLICATION(req, res) {
151 res->json(snapshot(req, "guarded_stats"));
152 });
153 app.use("/guarded", guarded);
154
155 // Fallback when router skipped
156 app.get("/guarded/:rest(.*)", [] APPLICATION(req, res) {
157 res->status(403).json({{"label", "guarded_fallback"}, {"status", 403}});
158 });
159
160 // Root-mounted router (baseUrl should be empty string)
161 const Router root;
162 root.get("/root/test", [] APPLICATION(req, res) {
163 res->json(snapshot(req, "root_mount"));
164 });
165 app.use("/", root);
166
167 // Nested routers trace: req.url / baseUrl stripping
168 const Router api;
169 api.use([] MIDDLEWARE(req, res, next) {
170 tracePush(req, "api.use");
171 next();
172 });
173
174 const Router v1;
175 v1.use([] MIDDLEWARE(req, res, next) {
176 tracePush(req, "v1.use");
177 next();
178 });
179 v1.get("/users/:id", [] APPLICATION(req, res) {
180 tracePush(req, "handler");
181 res->json({{"label", "nested_trace"}, {"trace", traceGet(req)}});
182 });
183
184 api.use("/v1", v1);
185 app.use("/api", api);
186
187 // mergeParams demo (requires your mergeParams patch-set)
188 const Router mpMerge;
189 mpMerge.setMergeParams(true);
190 mpMerge.get("/users/:id", [] APPLICATION(req, res) {
191 res->json(snapshot(req, "mp_merge"));
192 });
193 app.use("/mp/merge/t/:tenant", mpMerge);
194
195 const Router mpNoMerge;
196 mpNoMerge.get("/users/:id", [] APPLICATION(req, res) {
197 res->json(snapshot(req, "mp_nomerge"));
198 });
199 app.use("/mp/nomerge/t/:tenant", mpNoMerge);
200
201 // Params scoping trace: merge vs no-merge
202 auto makeScope = [](bool merge) {
203 const Router parent;
204 parent.setMergeParams();
205 parent.use([merge](auto const& req, auto const&, auto& next) {
206 tracePush(req, merge ? "scopeMerge.parent" : "scopeNoMerge.parent");
207 next();
208 });
209
210 const Router child;
211 child.setMergeParams(merge);
212 child.use([merge](auto const& req, auto const&, auto& next) {
213 tracePush(req, merge ? "scopeMerge.child" : "scopeNoMerge.child");
214 next();
215 });
216 child.get("/end", [merge](auto const& req, auto const& res) {
217 tracePush(req, merge ? "scopeMerge.handler" : "scopeNoMerge.handler");
218 res->json({{"label", merge ? "scope_merge" : "scope_nomerge"}, {"trace", traceGet(req)}});
219 });
220
221 parent.use("/b/:b", child);
222 return parent;
223 };
224
225 app.use("/scope/nomerge/:a", makeScope(false));
226 app.use("/scope/merge/:a", makeScope(true));
227
228 // Param decoding (valid)
229 app.get("/decode/:p", [] APPLICATION(req, res) {
230 res->json(snapshot(req, "decode_ok"));
231 });
232
233 // 404 (GET only, enough for this suite)
234 app.get("/:rest(.*)", [] APPLICATION(req, res) {
235 res->status(404).json({{"label", "not_found"}, {"path", req->path}});
236 });
237
238 app.listen(8080, [](const express::legacy::in::WebApp::SocketAddress& socketAddress, const core::socket::State& state) {
239 switch (state) {
241 VLOG(1) << "express-compat listening on '" << socketAddress.toString() << "'";
242 break;
244 VLOG(1) << "express-compat disabled";
245 break;
247 LOG(ERROR) << "express-compat " << socketAddress.toString() << ": " << state.what();
248 break;
250 LOG(FATAL) << "express-compat " << socketAddress.toString() << ": " << state.what();
251 break;
252 }
253 });
254
255 return core::SNodeC::start();
256}
#define APPLICATION(req, res)
Definition Router.h:68
#define MIDDLEWARE(req, res, next)
Definition Router.h:63
static void init(int argc, char *argv[])
Definition SNodeC.cpp:54
static int start(const utils::Timeval &timeOut={LONG_MAX, 0})
Definition SNodeC.cpp:60
static constexpr int DISABLED
Definition State.h:56
static constexpr int ERROR
Definition State.h:57
std::string what() const
Definition State.cpp:114
static constexpr int FATAL
Definition State.h:58
static constexpr int OK
Definition State.h:55
Route & use(const std::function< void(const std::shared_ptr< Request > &, const std::shared_ptr< Response > &)> &lambda) const
Definition Route.cpp:102
Route & get(const std::function< void(const std::shared_ptr< Request > &, const std::shared_ptr< Response > &)> &lambda) const
Definition Route.cpp:104
Route & use(const Router &router) const
Definition Router.cpp:100
Route & get(const Router &router) const
Definition Router.cpp:102
const Router & setMergeParams(bool mergeParams=true) const
Definition Router.cpp:90
typename Server::SocketAddress SocketAddress
Definition WebAppT.h:70
static void tracePush(const std::shared_ptr< express::Request > &req, const std::string &label)
static json snapshot(const std::shared_ptr< express::Request > &req, const std::string &label)
static json traceGet(const std::shared_ptr< express::Request > &req)
WebAppT< web::http::legacy::in::Server > WebApp
Definition WebApp.h:56

References core::socket::State::DISABLED, core::socket::State::ERROR, core::socket::State::FATAL, express::Router::get(), core::SNodeC::init(), express::Response::json(), net::in::stream::SocketServer< SocketAcceptorT, ConfigSocketServerT, SocketContextFactoryT, Args >::listen(), core::socket::State::OK, express::Next::operator()(), express::Request::params, express::Request::path, express::Response::send(), express::Response::set(), express::Router::setMergeParams(), snapshot(), core::SNodeC::start(), express::Response::status(), net::in::SocketAddress::toString(), traceGet(), tracePush(), express::WebAppT< ServerT >::WebAppT(), and core::socket::State::what().

Here is the call graph for this function:

◆ snapshot()

json snapshot ( const std::shared_ptr< express::Request > & req,
const std::string & label )
static

Definition at line 21 of file express_compat_server.cpp.

21 {
22 json out = json::object();
23 out["label"] = label;
24 out["method"] = req->method;
25 out["url"] = req->url;
26 out["originalUrl"] = req->originalUrl;
27 out["baseUrl"] = req->baseUrl;
28 out["path"] = req->path;
29
30 json params = json::object();
31 for (const auto& [k, v] : req->params) {
32 params[k] = v;
33 }
34 out["params"] = params;
35
36 json query = json::object();
37 for (const auto& [k, v] : req->queries) {
38 query[k] = v;
39 }
40 out["query"] = query;
41
42 json headers = json::object();
43 headers["x-test"] = req->get("X-Test");
44 out["headers"] = headers;
45
46 return out;
47}
std::string originalUrl
Definition Request.h:77
std::string url
Definition Request.h:98
std::map< std::string, std::string > params
Definition Request.h:81
const std::string & get(const std::string &key, int i=0) const
Definition Request.cpp:95
std::string method
Definition Request.h:97
std::string baseUrl
Definition Request.h:76
std::map< std::string, std::string > queries
Definition Request.h:103
std::string path
Definition Request.h:79

References express::Request::baseUrl, express::Request::get(), express::Request::method, express::Request::originalUrl, express::Request::params, express::Request::path, express::Request::queries, and express::Request::url.

Referenced by main(), and tracePush().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ traceGet()

json traceGet ( const std::shared_ptr< express::Request > & req)
static

Definition at line 63 of file express_compat_server.cpp.

63 {
64 json arr = json::array();
65 req->getAttribute<Trace>(
66 [&](Trace& t) {
67 for (auto const& e : t) {
68 arr.push_back(e);
69 }
70 },
71 "trace");
72 return arr;
73}
bool getAttribute(const std::function< void(Attribute &)> &onFound, const std::string &subKey="") const

References utils::MultibleAttributeInjector::getAttribute().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ tracePush()

void tracePush ( const std::shared_ptr< express::Request > & req,
const std::string & label )
static

Definition at line 54 of file express_compat_server.cpp.

54 {
55 ensureTrace(req);
56 req->getAttribute<Trace>(
57 [&](Trace& t) {
58 t.emplace_back(snapshot(req, label));
59 },
60 "trace");
61}
static void ensureTrace(const std::shared_ptr< express::Request > &req)

References ensureTrace(), utils::MultibleAttributeInjector::getAttribute(), and snapshot().

Referenced by main().

Here is the call graph for this function:
Here is the caller graph for this function: