From 70643a9e4821bd11c3f363f1708fe420965467d1 Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Fri, 28 Aug 2026 22:17:10 +0200 Subject: Format again --- server/src/http_server.cppm | 390 ++++++++++++++++++++++---------------------- 1 file changed, 192 insertions(+), 198 deletions(-) (limited to 'server/src/http_server.cppm') diff --git a/server/src/http_server.cppm b/server/src/http_server.cppm index 0f7fda7..366037f 100644 --- a/server/src/http_server.cppm +++ b/server/src/http_server.cppm @@ -41,7 +41,7 @@ private: virtual ~impl_base() = default; virtual auto header() -> bhttp::response_header& = 0; virtual auto header() const - -> bhttp::response_header const& = 0; + -> bhttp::response_header const& = 0; virtual auto is_done() const -> bool = 0; virtual auto prepare(beast::error_code&) -> const_buffers_type = 0; virtual auto consume(std::size_t n) -> void = 0; @@ -121,12 +121,12 @@ private: auto keep_alive() const noexcept -> bool override { return state_.visit( - util::overloaded{ - [](bhttp::response const& rsp) -> bool - { return rsp.keep_alive(); }, - [](bhttp::message_generator const& gen) -> bool - { return gen.keep_alive(); }, - }); + util::overloaded{ + [](bhttp::response const& rsp) -> bool + { return rsp.keep_alive(); }, + [](bhttp::message_generator const& gen) -> bool + { return gen.keep_alive(); }, + }); } }; @@ -164,15 +164,15 @@ using next_handler_t = std::functionnet::awaitable>; template using middleware_t = - std::function&, - next_handler_t) - ->net::awaitable>; + std::function&, + next_handler_t) + ->net::awaitable>; template auto lax_cors_middleware( - Ctx ctx, bhttp::request_header& req_hdr, - next_handler_t next) -> net::awaitable + Ctx ctx, bhttp::request_header& req_hdr, + next_handler_t next) -> net::awaitable { std::ignore = req_hdr; auto prersp = co_await next(ctx); @@ -188,16 +188,16 @@ struct trace_id_ctx : InnerCtx template auto trace_id_middleware( - OuterCtx ctx0, bhttp::request_header& req_hdr, - next_handler_t> next) -> net::awaitable + OuterCtx ctx0, bhttp::request_header& req_hdr, + next_handler_t> next) -> net::awaitable { std::ignore = req_hdr; auto ctx = trace_id_ctx{std::move(ctx0)}; auto prersp = co_await next(std::move(ctx)); prersp.header().set( - "X-Routemon-Trace-Id", std::string_view{ctx.trace_id.as_string()}); + "X-Routemon-Trace-Id", std::string_view{ctx.trace_id.as_string()}); prersp.header().insert( - bhttp::field::access_control_expose_headers, "X-Routemon-Trace-Id"); + bhttp::field::access_control_expose_headers, "X-Routemon-Trace-Id"); co_return std::move(prersp); } @@ -208,8 +208,8 @@ struct base_ctx template using basic_route_handler_fn_t = std::function< - auto(Ctx, readable_request, std::vector const& matches) - ->net::awaitable>; + auto(Ctx, readable_request, std::vector const& matches) + ->net::awaitable>; struct keep_alive { @@ -228,8 +228,8 @@ auto make_rsp(bhttp::status status, keep_alive ka) -> bhttp::response } auto problem_rsp( - base_ctx const& ctx, problem::details const& problem, keep_alive ka) - -> presponse + base_ctx const& ctx, problem::details const& problem, keep_alive ka) + -> presponse { auto rsp = make_rsp(problem.status, ka); rsp.set(bhttp::field::content_type, "application/problem+json"); @@ -244,18 +244,17 @@ struct preflight_response std::vector allow_headers; }; auto make_preflight_rsp(preflight_response res, keep_alive ka) - -> bhttp::response + -> bhttp::response { auto rsp = make_rsp(bhttp::status::no_content, ka); auto allow_headers_str = res.allow_headers | std::views::transform( - [](auto const& field) -> std::string_view - { return bhttp::to_string(field); }) + [](auto const& field) -> std::string_view + { return bhttp::to_string(field); }) | std::views::join_with(std::string_view{", "}) | std::ranges::to(); rsp.set( - bhttp::field::access_control_allow_methods, - res.allow_methods.to_string()); + bhttp::field::access_control_allow_methods, res.allow_methods.to_string()); rsp.set(bhttp::field::access_control_allow_headers, allow_headers_str); rsp.prepare_payload(); return rsp; @@ -266,7 +265,7 @@ auto make_preflight_rsp(preflight_response res, keep_alive ka) // anyway, but all context types should derive from base_ctx). template auto read_request(base_ctx const& ctx, readable_request&& r) - -> net::awaitable, presponse>> + -> net::awaitable, presponse>> { std::ignore = ctx; auto p = bhttp::request_parser{std::move(*r.p)}; @@ -276,20 +275,19 @@ auto read_request(base_ctx const& ctx, readable_request&& r) template <> auto read_request(base_ctx const& ctx, readable_request&& r) - -> net::awaitable< - std::expected, presponse>> + -> net::awaitable, presponse>> { auto [ec, _] = - co_await bhttp::async_read(*r.strm, *r.buf, *r.p, net::as_tuple); + co_await bhttp::async_read(*r.strm, *r.buf, *r.p, net::as_tuple); if (ec == bhttp::error::unexpected_body) { auto tpl = problem::tpl{ - .status = bhttp::status::bad_request, - .title = translate("No body expected for this request"), - .type_uri = "https://routemon.fautchen.eu/problems/unexpected-body", + .status = bhttp::status::bad_request, + .title = translate("No body expected for this request"), + .type_uri = "https://routemon.fautchen.eu/problems/unexpected-body", }; co_return std::unexpected{problem_rsp( - ctx, tpl.instantiate(), keep_alive{false})}; + ctx, tpl.instantiate(), keep_alive{false})}; } else if (ec) { @@ -310,8 +308,8 @@ template [](routed_ctx const&) {}(ctx); } auto default_options_handler( - Ctx const& ctx, readable_request r, std::vector const&) - -> net::awaitable + Ctx const& ctx, readable_request r, std::vector const&) + -> net::awaitable { auto mreq = co_await read_request(ctx, std::move(r)); if (!mreq) @@ -321,18 +319,18 @@ auto default_options_handler( { // CORS preflight request co_return make_preflight_rsp( - preflight_response{ - // TODO: should access-control-allow-methods contain OPTIONS? - .allow_methods = ctx.route_methods, - .allow_headers = {bhttp::field::content_type}, - }, - keep_alive{mreq->keep_alive()}); + preflight_response{ + // TODO: should access-control-allow-methods contain OPTIONS? + .allow_methods = ctx.route_methods, + .allow_headers = {bhttp::field::content_type}, + }, + keep_alive{mreq->keep_alive()}); } else { // Normal OPTIONS request auto rsp = make_rsp( - bhttp::status::no_content, keep_alive{mreq->keep_alive()}); + bhttp::status::no_content, keep_alive{mreq->keep_alive()}); rsp.set(bhttp::field::allow, ctx.route_methods.to_string()); rsp.prepare_payload(); co_return std::move(rsp); @@ -340,7 +338,7 @@ auto default_options_handler( } auto global_options_handler(base_ctx const& ctx, readable_request r) - -> net::awaitable + -> net::awaitable { // TODO: switch to "small (4KB) discarded" body type, similar to what Go // does? Same goes for default_options_handler? Not sure. @@ -349,46 +347,45 @@ auto global_options_handler(base_ctx const& ctx, readable_request r) co_return std::move(res.error()); auto req = r.p->release(); auto rsp = make_rsp( - bhttp::status::no_content, keep_alive{req.keep_alive()}); + bhttp::status::no_content, keep_alive{req.keep_alive()}); rsp.prepare_payload(); co_return std::move(rsp); } template auto id_middleware( - Ctx ctx, bhttp::request_header&, next_handler_t next) - -> net::awaitable + Ctx ctx, bhttp::request_header&, next_handler_t next) + -> net::awaitable { co_return co_await next(std::move(ctx)); } template auto middleware_compose(middleware_t ab, middleware_t bc) - -> middleware_t + -> middleware_t { return [ab = std::move(ab), bc = std::move(bc)]( - A a, bhttp::request_header& header, - next_handler_t next) -> net::awaitable + A a, bhttp::request_header& header, + next_handler_t next) -> net::awaitable { co_return co_await ab( - std::move(a), header, [&](B b) -> net::awaitable - { co_return co_await bc(std::move(b), header, next); }); + std::move(a), header, [&](B b) -> net::awaitable + { co_return co_await bc(std::move(b), header, next); }); }; } template auto middleware_wrap_fn(middleware_t ab, basic_route_handler_fn_t fn) - -> basic_route_handler_fn_t + -> basic_route_handler_fn_t { - return - [ab = std::move(ab), fn = std::move(fn)]( - A a_ctx, readable_request r, - std::vector const& matches) -> net::awaitable + return [ab = std::move(ab), fn = std::move(fn)]( + A a_ctx, readable_request r, + std::vector const& matches) -> net::awaitable { co_return co_await ab( - std::move(a_ctx), r.p->get().base(), - [&](B b_ctx) -> net::awaitable - { co_return co_await fn(std::move(b_ctx), r, matches); }); + std::move(a_ctx), r.p->get().base(), + [&](B b_ctx) -> net::awaitable + { co_return co_await fn(std::move(b_ctx), r, matches); }); }; } @@ -448,15 +445,15 @@ struct handler_map template auto map(std::invocable auto f) const -> handler_map requires std::assignable_from< - U&, std::invoke_result_t> + U&, std::invoke_result_t> { return { - .options = static_cast(options) ? f(options) : U{}, - .delete_ = static_cast(delete_) ? f(delete_) : U{}, - .get = static_cast(get) ? f(get) : U{}, - .head = static_cast(head) ? f(head) : U{}, - .post = static_cast(post) ? f(post) : U{}, - .put = static_cast(put) ? f(put) : U{}, + .options = static_cast(options) ? f(options) : U{}, + .delete_ = static_cast(delete_) ? f(delete_) : U{}, + .get = static_cast(get) ? f(get) : U{}, + .head = static_cast(head) ? f(head) : U{}, + .post = static_cast(post) ? f(post) : U{}, + .put = static_cast(put) ? f(put) : U{}, }; } }; @@ -478,29 +475,29 @@ struct route_tree template auto middleware_wrap_tree( - middleware_t mw, route_tree const& tree) - -> route_tree + middleware_t mw, route_tree const& tree) + -> route_tree { auto new_leaves = tree.here.template map>( - std::bind_front(middleware_wrap_fn, mw)); + std::bind_front(middleware_wrap_fn, mw)); auto new_sub = tree.sub.visit( - util::overloaded{ - [&mw](route_tree::named_subtrees const& subtrees) - -> decltype(route_tree::sub) - { - auto new_subtrees = typename route_tree::named_subtrees{}; - for (auto [seg, subtree] : subtrees) - new_subtrees[seg] = middleware_wrap_tree(mw, subtree); - return new_subtrees; - }, - [&mw](route_tree::wildcard_subtree const& subtree) - -> decltype(route_tree::sub) - { - return typename route_tree::wildcard_subtree{ - middleware_wrap_tree(mw, *subtree) - }; - }, - }); + util::overloaded{ + [&mw](route_tree::named_subtrees const& subtrees) + -> decltype(route_tree::sub) + { + auto new_subtrees = typename route_tree::named_subtrees{}; + for (auto [seg, subtree] : subtrees) + new_subtrees[seg] = middleware_wrap_tree(mw, subtree); + return new_subtrees; + }, + [&mw](route_tree::wildcard_subtree const& subtree) + -> decltype(route_tree::sub) + { + return typename route_tree::wildcard_subtree{ + middleware_wrap_tree(mw, *subtree) + }; + }, + }); return {.here = new_leaves, .sub = new_sub}; } @@ -509,23 +506,21 @@ concept match_arg = std::constructible_from; template using route_handler_fn_t = std::function< - auto(Ctx, readable_request, MatchArgs...)->net::awaitable>; + auto(Ctx, readable_request, MatchArgs...)->net::awaitable>; template auto degen_route_handler(route_handler_fn_t fn) - -> basic_route_handler_fn_t + -> basic_route_handler_fn_t { - return - [fn = std::move(fn)]( - Ctx ctx, readable_request r, - std::vector const& matches) -> net::awaitable + return [fn = std::move(fn)]( + Ctx ctx, readable_request r, + std::vector const& matches) -> net::awaitable { if (sizeof...(MatchArgs) != matches.size()) throw std::runtime_error{"got unexpected amount of matches"}; auto it = matches.begin(); co_return co_await fn( - std::move(ctx), r, - MatchArgs{static_cast(*it++)}...); + std::move(ctx), r, MatchArgs{static_cast(*it++)}...); }; } @@ -534,7 +529,7 @@ struct ctree : route_tree { template auto wrap(middleware_t mw) const - -> ctree + -> ctree { return {middleware_wrap_tree(std::move(mw), *this)}; } @@ -546,25 +541,24 @@ struct dtree : handler_map> [[nodiscard]] auto to_leaves() const -> typename route_tree::leaves { auto here = this->template map>( - degen_route_handler); + degen_route_handler); if (!here.verbs().empty() && !static_cast(this->options)) here.options = default_options_handler; return here; } [[nodiscard]] auto named_subtrees( - std::initializer_list>> - subtrees) const -> ctree + std::initializer_list>> + subtrees) const -> ctree { auto sub = typename route_tree::named_subtrees{ - std::from_range, subtrees - | std::views::transform( - [](auto const& p) - { - return std::make_pair( - p.first, - static_cast>(p.second)); - }) + std::from_range, subtrees + | std::views::transform( + [](auto const& p) + { + return std::make_pair( + p.first, static_cast>(p.second)); + }) }; return {route_tree{.here = to_leaves(), .sub = sub}}; } @@ -572,13 +566,13 @@ struct dtree : handler_map> template [[nodiscard]] auto wildcard_subtree(ctree subtree) - -> ctree + -> ctree { return {route_tree{ - .here = to_leaves(), - .sub = typename route_tree::wildcard_subtree{ - static_cast>(subtree) - } + .here = to_leaves(), + .sub = typename route_tree::wildcard_subtree{ + static_cast>(subtree) + } }}; } @@ -598,9 +592,9 @@ class server public: explicit server( - log::logger const& l, locale::selector&& lsel, - middleware_t global_middleware, - route_tree> routes) + log::logger const& l, locale::selector&& lsel, + middleware_t global_middleware, + route_tree> routes) : l_{l.sub("http_server")}, lsel_{std::move(lsel)}, global_middleware_{std::move(global_middleware)}, routes_{std::move(routes)} @@ -610,55 +604,56 @@ public: struct match_result { util::not_null< - handler_map>> const*> - route_handlers; + handler_map>> const*> + route_handlers; std::vector wildcard_matches; auto allowed_methods() const -> verb_set { return route_handlers->verbs(); } }; auto match(boost::urls::segments_view segments) const - -> std::optional + -> std::optional { auto const* tree = &routes_; auto wildcard_matches = std::vector{}; for (auto const& seg : segments) { tree->sub.visit( - util::overloaded{ - [&](route_tree>::named_subtrees const& - subtrees) - { - auto it = subtrees.find(seg); - tree = it == subtrees.end() ? nullptr : &it->second; - }, - [&](route_tree>::wildcard_subtree const& - wildcard_subtree) - { - wildcard_matches.push_back(seg); - tree = &*wildcard_subtree; - }, - }); + util::overloaded{ + [&]( + route_tree>::named_subtrees const& subtrees) + { + auto it = subtrees.find(seg); + tree = it == subtrees.end() ? nullptr : &it->second; + }, + [&]( + route_tree>::wildcard_subtree const& + wildcard_subtree) + { + wildcard_matches.push_back(seg); + tree = &*wildcard_subtree; + }, + }); if (!tree) return std::nullopt; } if (tree->here.empty()) return std::nullopt; return match_result{ - .route_handlers = util::not_null{&tree->here}, - .wildcard_matches = wildcard_matches, + .route_handlers = util::not_null{&tree->here}, + .wildcard_matches = wildcard_matches, }; } auto route_request(PreRouteCtx ctx, readable_request r) const - -> net::awaitable + -> net::awaitable { auto req_base = r.p->get().base(); auto const bad_request_tpl = problem::tpl{ - .status = bhttp::status::bad_request, - .title = translate("Bad request"), - .type_uri = "https://routemon.fautchen.eu/problems/bad-request", + .status = bhttp::status::bad_request, + .title = translate("Bad request"), + .type_uri = "https://routemon.fautchen.eu/problems/bad-request", }; if (req_base.target() == "*") @@ -669,10 +664,10 @@ public: if (req_base.method() != bhttp::verb::options) { auto tpl = problem::tpl{ - .status = bhttp::status::method_not_allowed, - .title = translate("Method not allowed"), - .type_uri = "https://routemon.fautchen.eu/problems/" - "method-not-allowed", + .status = bhttp::status::method_not_allowed, + .title = translate("Method not allowed"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "method-not-allowed", }; co_return problem_rsp(ctx, tpl.instantiate(), keep_alive{false}); } @@ -690,10 +685,10 @@ public: if (!req_url.is_path_absolute()) { auto problem = bad_request_tpl.instantiate().set_detail(translate( - "Path of normalized (RFC 3986, § 6) " - "origin-form request-target (RFC " - "9112, § 3.2.1) should be " - "absolute")); + "Path of normalized (RFC 3986, § 6) " + "origin-form request-target (RFC " + "9112, § 3.2.1) should be " + "absolute")); co_return problem_rsp(ctx, problem, keep_alive{false}); } @@ -701,9 +696,9 @@ public: if (!mres) { auto tpl = problem::tpl{ - .status = bhttp::status::not_found, - .title = translate("Not found"), - .type_uri = "https://routemon.fautchen.eu/problems/not-found", + .status = bhttp::status::not_found, + .title = translate("Not found"), + .type_uri = "https://routemon.fautchen.eu/problems/not-found", }; co_return problem_rsp(ctx, tpl.instantiate(), keep_alive{false}); } @@ -713,10 +708,10 @@ public: { // Method not implemented. auto tpl = problem::tpl{ - .status = bhttp::status::not_implemented, - .title = translate("Method not implemented"), - .type_uri = "https://routemon.fautchen.eu/problems/" - "method-not-implemented", + .status = bhttp::status::not_implemented, + .title = translate("Method not implemented"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "method-not-implemented", }; co_return problem_rsp(ctx, tpl.instantiate(), keep_alive{false}); } @@ -724,21 +719,21 @@ public: if (auto mhdl = mres->route_handlers->lookup(*mverb)) { auto new_ctx = - routed_ctx{std::move(ctx), mres->allowed_methods()}; + routed_ctx{std::move(ctx), mres->allowed_methods()}; co_return co_await mhdl(std::move(new_ctx), r, mres->wildcard_matches); } else { // Path recognized, but method not allowed. auto tpl = problem::tpl{ - .status = bhttp::status::method_not_allowed, - .title = translate("Method not allowed"), - .type_uri = "https://routemon.fautchen.eu/problems/" - "method-not-allowed", + .status = bhttp::status::method_not_allowed, + .title = translate("Method not allowed"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "method-not-allowed", }; auto rsp = problem_rsp(ctx, tpl.instantiate(), keep_alive{false}); rsp.header().set( - bhttp::field::allow, mres->allowed_methods().to_string()); + bhttp::field::allow, mres->allowed_methods().to_string()); co_return std::move(rsp); } } @@ -747,9 +742,9 @@ public: // We do not accept any other request-target forms. auto problem = bad_request_tpl.instantiate().set_detail(translate( - "Invalid request-target, expected " - "asterisk-form or origin-form " - "(see RFC 9112, § 3.2)")); + "Invalid request-target, expected " + "asterisk-form or origin-form " + "(see RFC 9112, § 3.2)")); co_return problem_rsp(ctx, problem, keep_alive{false}); } } @@ -761,9 +756,8 @@ public: auto ctx0 = base_ctx{.locale = locale}; co_return co_await global_middleware_( - std::move(ctx0), header, - [&](PreRouteCtx ctx) -> net::awaitable - { co_return co_await route_request(std::move(ctx), std::move(r)); }); + std::move(ctx0), header, [&](PreRouteCtx ctx) -> net::awaitable + { co_return co_await route_request(std::move(ctx), std::move(r)); }); } auto do_session(beast::tcp_stream strm) -> net::awaitable @@ -775,7 +769,7 @@ public: auto p0 = bhttp::request_parser{}; p0.body_limit(boost::none); auto [ec, _] = - co_await bhttp::async_read_header(strm, buf, p0, net::as_tuple); + co_await bhttp::async_read_header(strm, buf, p0, net::as_tuple); if (ec == bhttp::error::end_of_stream) break; else if (ec) @@ -783,11 +777,11 @@ public: auto http_version = p0.get().version(); auto&& rsp = co_await handle_request( - readable_request{ - .p = util::not_null{&p0}, - .strm = util::not_null{&strm}, - .buf = util::not_null{&buf}, - }); + readable_request{ + .p = util::not_null{&p0}, + .strm = util::not_null{&strm}, + .buf = util::not_null{&buf}, + }); rsp.header().version(http_version); bool keep_alive = rsp.keep_alive(); co_await beast::async_write(strm, std::move(rsp)); @@ -806,27 +800,27 @@ public: auto acceptor = tcp::acceptor{executor, endpoint}; l_.with("endpoint", endpoint.address().to_string()) - .with("port", std::to_string(endpoint.port())) - .info("Serving"); + .with("port", std::to_string(endpoint.port())) + .info("Serving"); while (true) { net::co_spawn( - executor, - do_session(beast::tcp_stream{co_await acceptor.async_accept()}), - [this](std::exception_ptr e) + executor, + do_session(beast::tcp_stream{co_await acceptor.async_accept()}), + [this](std::exception_ptr e) + { + if (e) { - if (e) + try + { + std::rethrow_exception(e); + } + catch (std::exception const& e) { - try - { - std::rethrow_exception(e); - } - catch (std::exception const& e) - { - l_.error("Error in session: {}", e.what()); - } + l_.error("Error in session: {}", e.what()); } - }); + } + }); } } @@ -837,21 +831,21 @@ public: // TODO: make exception handling as nice as in srv.cpp net::co_spawn( - ioc, do_listen(endpoint), - [this](std::exception_ptr e) + ioc, do_listen(endpoint), + [this](std::exception_ptr e) + { + if (e) { - if (e) + try { - try - { - std::rethrow_exception(e); - } - catch (std::exception const& e) - { - l_.error("Error: {}", e.what()); - } + std::rethrow_exception(e); } - }); + catch (std::exception const& e) + { + l_.error("Error: {}", e.what()); + } + } + }); } }; -- cgit v1.3