Skip to content

Commit 0dae471

Browse files
aff3npirataff3npiratfelixguendling
authored
Ep response handler (#32)
* Initial Commit * Determine string/json post via template deduction * Add previous implementation * Old api wraps new POST/GET methods * Reformat * Cleanup reuse existing concepts when possible * formatting, spelling --------- Co-authored-by: aff3npirat <jurek.elliesen@web.de> Co-authored-by: Felix Gündling <felix.guendling@gmail.com>
1 parent bb00caf commit 0dae471

1 file changed

Lines changed: 81 additions & 31 deletions

File tree

include/net/web_server/query_router.h

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <iostream>
55
#include <memory>
66
#include <string>
7+
#include <type_traits>
78
#include <utility>
89
#include <vector>
910

@@ -45,24 +46,43 @@ concept JSON =
4546
boost::json::has_value_from<T>::value &&
4647
!std::is_same_v<T, std::string>; // avoid ambiguity with string handlers
4748

49+
template <typename T>
50+
concept StatusResponse =
51+
std::is_same_v<typename T::first_type, boost::beast::http::status>;
52+
53+
template <typename T>
54+
concept ContentOnlyResponse = !StatusResponse<T>;
55+
56+
template <typename Fn>
57+
concept ContentOnlyHandler = requires(Fn f, utl::first_argument<Fn> arg) {
58+
{ f(arg) } -> std::same_as<std::string>;
59+
} || requires(Fn f, utl::first_argument<Fn> arg) {
60+
{ f(arg) } -> ContentOnlyResponse; // avoid ambiguity with json handlers
61+
{ f(arg) } -> JSON;
62+
};
63+
4864
template <typename Fn>
4965
concept StringGetHandler = requires(boost::urls::url_view const& url, Fn f) {
50-
{ f(url) } -> std::same_as<std::string>;
66+
{ f(url) } -> StatusResponse;
67+
{ f(url).second } -> std::same_as<std::string>;
5168
};
5269

5370
template <typename Fn>
5471
concept StringPostHandler = requires(std::string_view const& req, Fn f) {
55-
{ f(req) } -> std::same_as<std::string>;
72+
{ f(req) } -> StatusResponse;
73+
{ f(req).second } -> std::same_as<std::string>;
5674
};
5775

5876
template <typename Fn>
59-
concept JsonPostHandler = requires(Fn f, typename utl::first_argument<Fn> arg) {
60-
{ f(arg) } -> JSON;
77+
concept JsonPostHandler = requires(Fn f, utl::first_argument<Fn> arg) {
78+
{ f(arg) } -> StatusResponse;
79+
{ f(arg).second } -> JSON;
6180
};
6281

6382
template <typename Fn>
6483
concept JsonGetHandler = requires(boost::urls::url_view const& url, Fn f) {
65-
{ f(url) } -> JSON;
84+
{ f(url) } -> StatusResponse;
85+
{ f(url).second } -> JSON;
6686
};
6787

6888
struct default_exec {
@@ -169,7 +189,10 @@ struct query_router {
169189
return *this;
170190
}
171191

172-
template <StringPostHandler Fn>
192+
template <typename Fn>
193+
requires requires(std::string_view const& req, Fn f) {
194+
{ f(req) } -> std::same_as<std::string>;
195+
}
173196
query_router& route(std::string method, std::string const& path_regex,
174197
Fn&& fn) {
175198
return route(std::move(method), path_regex,
@@ -183,19 +206,47 @@ struct query_router {
183206
});
184207
}
185208

209+
template <ContentOnlyHandler Fn>
210+
query_router& get(std::string const& path_regex, Fn&& fn) {
211+
return get(path_regex,
212+
[fn = std::forward<Fn>(fn)](boost::urls::url_view const& url) {
213+
return std::make_pair(boost::beast::http::status::ok, fn(url));
214+
});
215+
}
216+
217+
template <ContentOnlyHandler Fn>
218+
query_router& post(std::string const& path_regex, Fn&& fn) {
219+
return post(path_regex, [fn = std::forward<Fn>(fn)](
220+
typename utl::first_argument<Fn> arg) {
221+
return std::make_pair(boost::beast::http::status::ok, fn(arg));
222+
});
223+
}
224+
225+
template <StringPostHandler Fn>
226+
query_router& post(std::string const& path_regex, Fn&& fn) {
227+
return route("POST", path_regex,
228+
[fn = std::forward<Fn>(fn)](web_server::http_req_t const& req,
229+
bool is_ssl) {
230+
auto [status, content] = fn(req.body());
231+
auto res =
232+
net::web_server::string_res_t{status, req.version()};
233+
set_response_body(res, req, content);
234+
res.keep_alive(req.keep_alive());
235+
return res;
236+
});
237+
}
238+
186239
template <StringGetHandler Fn>
187240
query_router& get(std::string const& path_regex, Fn&& fn) {
188-
namespace http = boost::beast::http;
189-
namespace json = boost::json;
190-
return route(
191-
"GET", path_regex,
192-
[fn = std::forward<Fn>(fn)](route_request const& req,
193-
bool const ssl) -> reply {
194-
auto res = web_server::string_res_t{http::status::ok, req.version()};
195-
set_response_body(res, req, fn(boost::url_view{req.target()}));
196-
res.keep_alive(req.keep_alive());
197-
return res;
198-
});
241+
return route("GET", path_regex,
242+
[fn = std::forward<Fn>(fn)](route_request const& req, bool) {
243+
auto [status, content] = fn(boost::url_view{req.target()});
244+
245+
auto res = web_server::string_res_t{status, req.version()};
246+
set_response_body(res, req, content);
247+
res.keep_alive(req.keep_alive());
248+
return res;
249+
});
199250
}
200251

201252
template <JsonPostHandler Fn>
@@ -204,32 +255,31 @@ struct query_router {
204255
"POST", path_regex,
205256
[fn = std::forward<Fn>(fn)](web_server::http_req_t const& req,
206257
bool is_ssl) -> reply {
207-
auto res = net::web_server::string_res_t{
208-
boost::beast::http::status::ok, req.version()};
258+
auto [status, content] =
259+
fn(boost::json::value_to<std::decay_t<utl::first_argument<Fn>>>(
260+
boost::json::parse(req.body())));
261+
auto res = net::web_server::string_res_t{status, req.version()};
209262
res.set(boost::beast::http::field::content_type, "application/json");
210263
set_response_body(
211264
res, req,
212-
boost::json::serialize(boost::json::value_from(fn(
213-
boost::json::value_to<std::decay_t<utl::first_argument<Fn>>>(
214-
boost::json::parse(req.body()))))));
265+
boost::json::serialize(boost::json::value_from(content)));
215266
res.keep_alive(req.keep_alive());
216267
return res;
217268
});
218269
}
219270

220271
template <JsonGetHandler Fn>
221272
query_router& get(std::string const& path_regex, Fn&& fn) {
222-
namespace http = boost::beast::http;
223273
namespace json = boost::json;
224274
return route("GET", path_regex,
225-
[fn = std::forward<Fn>(fn)](route_request const& req,
226-
bool const ssl) -> reply {
227-
auto res = web_server::string_res_t{http::status::ok,
228-
req.version()};
229-
res.set(http::field::content_type, "application/json");
230-
set_response_body(res, req,
231-
json::serialize(json::value_from(
232-
fn(boost::url_view{req.target()}))));
275+
[fn = std::forward<Fn>(fn)](route_request const& req, bool) {
276+
auto [status, content] = fn(boost::url_view{req.target()});
277+
278+
auto res = web_server::string_res_t{status, req.version()};
279+
res.set(boost::beast::http::field::content_type,
280+
"application/json");
281+
set_response_body(
282+
res, req, json::serialize(json::value_from(content)));
233283
res.keep_alive(req.keep_alive());
234284
return res;
235285
});

0 commit comments

Comments
 (0)