From ecf1be3f834c7187312f8eee71f77e448c995388 Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Tue, 22 Sep 2026 22:28:10 +0200 Subject: Check period selection --- server/src/api.cpp | 11 ++--- server/src/api.cppm | 3 +- server/src/geo/wgs84.cppm | 3 -- server/src/http_server.cppm | 18 +++---- server/src/srv.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 126 insertions(+), 22 deletions(-) (limited to 'server/src') diff --git a/server/src/api.cpp b/server/src/api.cpp index f1ca24e..9a51690 100644 --- a/server/src/api.cpp +++ b/server/src/api.cpp @@ -141,20 +141,15 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) auto const dur_build_s = chrono::duration{after_build - before_build}; l_.info("Indexes built in {}", dur_build_s); l_.info("BLSE index size: {}", blse_index_.size()); - l_.info("Point index size: {}", bpe_index_.size()); + l_.info("BPE index size: {}", bpe_index_.size()); } -auto handler::process_gpx(trace::id trace_id, gpx::file&& gpx_file) +auto handler::process_gpx( + trace::id trace_id, gpx::file&& gpx_file, time::period_seq check_periods) -> std::optional { auto l = l_.with("trace_id", trace_id.as_string()); - auto const now = chrono::utc_clock::now(); - auto const relevant = std::initializer_list{ - time::period{now - chrono::days(7), now + chrono::days(7)} - }; - auto const check_periods = time::period_seq{relevant.begin(), relevant.end()}; - // TODO: eliminate use of overlap segments auto segments = std::vector{}; for (auto const& track : gpx_file.tracks) diff --git a/server/src/api.cppm b/server/src/api.cppm index b2744cb..b14bbcf 100644 --- a/server/src/api.cppm +++ b/server/src/api.cppm @@ -93,7 +93,8 @@ class handler public: explicit handler(log::logger const& l, datex2::situation_publication pub); - auto process_gpx(trace::id trace_id, gpx::file&& gpx_file) + auto process_gpx( + trace::id trace_id, gpx::file&& gpx_file, time::period_seq check_periods) -> std::optional; auto sysinfo() -> sysinfo; }; diff --git a/server/src/geo/wgs84.cppm b/server/src/geo/wgs84.cppm index b53a963..2212a92 100644 --- a/server/src/geo/wgs84.cppm +++ b/server/src/geo/wgs84.cppm @@ -10,9 +10,6 @@ namespace routemon::geo::wgs84 { using cs = bgeo::cs::geographic; using point = bgeo::model::point; -// TODO: guarantee that linestring provides non-static member -// auto reserve(std::size_t) -> void -// Perhaps better yet: guarantee that the backing container is a std::vector. using linestring = bgeo::model::linestring; using box = bgeo::model::box; diff --git a/server/src/http_server.cppm b/server/src/http_server.cppm index e70110e..c111118 100644 --- a/server/src/http_server.cppm +++ b/server/src/http_server.cppm @@ -300,6 +300,7 @@ template struct routed_ctx : InnerCtx { verb_set route_methods; + boost::urls::url target; }; template @@ -687,12 +688,12 @@ class router co_return problem_rsp(ctx, problem, keep_alive{false}); } - l_.with( - "trace_id", - static_cast(ctx).trace_id.as_string()) - .debug( - "Request targets {} {}", req_base.method_string(), - req_url.path()); + // l_.with( + // "trace_id", + // static_cast(ctx).trace_id.as_string()) + // .debug( + // "Request targets {} {}", req_base.method_string(), + // req_url.path()); auto mres = match(req_url.segments()); if (!mres) @@ -720,8 +721,9 @@ class router if (auto mhdl = mres->route_handlers->lookup(*mverb)) { - auto new_ctx = - routed_ctx{std::move(ctx), mres->allowed_methods()}; + auto new_ctx = routed_ctx{ + std::move(ctx), mres->allowed_methods(), std::move(req_url) + }; co_return co_await mhdl( std::move(new_ctx), r, mres->wildcard_matches); } diff --git a/server/src/srv.cpp b/server/src/srv.cpp index dc9c814..5de0fc5 100644 --- a/server/src/srv.cpp +++ b/server/src/srv.cpp @@ -30,7 +30,7 @@ namespace routemon::srv { class gpx_parse_error_category_impl : public std::error_category { public: - char const* name() const noexcept override { return "gpx_parse"; } + auto name() const noexcept -> char const* override { return "gpx_parse"; } auto message(int condition) const noexcept -> std::string override { @@ -330,12 +330,120 @@ struct raw_deflate_body }; }; +auto parse_timestamp(std::string_view in) -> std::optional +{ + auto res = time::timestamp{}; + auto is = std::istringstream{in}; + is >> std::chrono::parse("%Y-%m-%dT%H:%MZ", res); + return is.fail() ? std::nullopt : std::make_optional(res); +} + auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) -> net::awaitable { auto l = l_.sub("handle_process_gpx").with("trace_id", ctx.trace_id.as_string()); + auto cp_begin = time::timestamp{}; + auto cp_end = time::timestamp{}; + + auto const params = ctx.target.params(); + if (auto it = params.find("cpbegin"); it != params.end()) + { + auto mts = parse_timestamp((*it).value); + if (!mts) + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Query parameter has bad value"), + .type_uri = + "https://routemon.fautchen.eu/problems/query-param-bad-value", + }; + co_return http::problem_rsp( + ctx, tpl.instantiate(), http::keep_alive{false}); + } + cp_begin = *mts; + + ++it; + if (it != params.end() && (*it).key == "cpbegin") + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Query parameter provided too many times"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "query-param-too-many-occurrences", + }; + co_return http::problem_rsp( + ctx, tpl.instantiate(), http::keep_alive{false}); + } + } + else + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Required query parameter not provided"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "required-query-param-not-provided", + }; + co_return http::problem_rsp( + ctx, tpl.instantiate(), http::keep_alive{false}); + } + if (auto it = params.find("cpend"); it != params.end()) + { + auto mts = parse_timestamp((*it).value); + if (!mts) + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Query parameter has bad value"), + .type_uri = + "https://routemon.fautchen.eu/problems/query-param-bad-value", + }; + co_return http::problem_rsp( + ctx, tpl.instantiate(), http::keep_alive{false}); + } + cp_end = *mts; + + ++it; + if (it != params.end() && (*it).key == "cpend") + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Query parameter provided too many times"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "query-param-too-many-occurrences", + }; + co_return http::problem_rsp( + ctx, tpl.instantiate(), http::keep_alive{false}); + } + } + else + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Required query parameter not provided"), + .type_uri = "https://routemon.fautchen.eu/problems/" + "required-query-param-not-provided", + }; + co_return http::problem_rsp( + ctx, tpl.instantiate(), http::keep_alive{false}); + } + + if (cp_begin >= cp_end) + { + auto tpl = problem::tpl{ + .status = bhttp::status::bad_request, + .title = translate("Invalid check period provided"), + .type_uri = "https://routemon.fautchen.eu/problems/invalid-check-period", + }; + co_return http::problem_rsp( + ctx, + tpl.instantiate().set_detail(translate( + "Begin of check period must be before end of check period")), + http::keep_alive{false}); + } + auto cp = time::period{cp_begin, cp_end}; + auto gpx_file = gpx::file{}; try { @@ -378,7 +486,8 @@ auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) // TODO: catch handler exceptions and return 500 when raised? // (keep-alive depends on whether whole request was read) - auto mres = inner_.process_gpx(ctx.trace_id, std::move(gpx_file)); + auto mres = inner_.process_gpx( + ctx.trace_id, std::move(gpx_file), time::period_seq{cp}); if (!mres) { auto tpl = problem::tpl{ -- cgit v1.3