diff options
| author | Rutger Broekhoff | 2026-09-10 10:59:59 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2026-09-10 10:59:59 +0200 |
| commit | a4408b5ac4ec10421bf9a7d5bf277946ec9f786e (patch) | |
| tree | 0a5214580a1557b3eb85f0a3ab0e15c5c9ec9f50 /server/src | |
| parent | 4031e77edd2abd694114be698312d086bce6b425 (diff) | |
| download | routemon-main.tar.gz routemon-main.zip | |
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/http_server.cppm | 20 | ||||
| -rw-r--r-- | server/src/locale.cpp | 121 | ||||
| -rw-r--r-- | server/src/locale.cppm | 65 | ||||
| -rw-r--r-- | server/src/main.cpp | 3 | ||||
| -rw-r--r-- | server/src/srv.cpp | 88 |
5 files changed, 178 insertions, 119 deletions
diff --git a/server/src/http_server.cppm b/server/src/http_server.cppm index 12c83db..2bf3c63 100644 --- a/server/src/http_server.cppm +++ b/server/src/http_server.cppm | |||
| @@ -200,7 +200,7 @@ auto cors_middleware(std::vector<std::string> const& allow_origins) | |||
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | template <class Ctx> | 202 | template <class Ctx> |
| 203 | auto trace_id_middleware( | 203 | auto expose_trace_id_middleware( |
| 204 | Ctx ctx, bhttp::request_header<bhttp::fields>& req_hdr, | 204 | Ctx ctx, bhttp::request_header<bhttp::fields>& req_hdr, |
| 205 | next_handler_t<Ctx> next) -> net::awaitable<presponse> | 205 | next_handler_t<Ctx> next) -> net::awaitable<presponse> |
| 206 | { | 206 | { |
| @@ -214,6 +214,24 @@ auto trace_id_middleware( | |||
| 214 | co_return std::move(prersp); | 214 | co_return std::move(prersp); |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | template <class Ctx> | ||
| 218 | auto expose_content_language_middleware( | ||
| 219 | Ctx ctx, bhttp::request_header<bhttp::fields>& req_hdr, | ||
| 220 | next_handler_t<Ctx> next) -> net::awaitable<presponse> | ||
| 221 | { | ||
| 222 | std::ignore = req_hdr; | ||
| 223 | auto prersp = co_await next(std::move(ctx)); | ||
| 224 | if (prersp.header().find(bhttp::field::content_language) | ||
| 225 | == prersp.header().end() | ||
| 226 | && std::has_facet<locale::bcp47_tag>(ctx.locale)) | ||
| 227 | { | ||
| 228 | prersp.header().set( | ||
| 229 | bhttp::field::content_language, | ||
| 230 | std::use_facet<locale::bcp47_tag>(ctx.locale).tag()); | ||
| 231 | } | ||
| 232 | co_return std::move(prersp); | ||
| 233 | } | ||
| 234 | |||
| 217 | struct keep_alive | 235 | struct keep_alive |
| 218 | { | 236 | { |
| 219 | bool value; | 237 | bool value; |
diff --git a/server/src/locale.cpp b/server/src/locale.cpp index 652349c..92bc33b 100644 --- a/server/src/locale.cpp +++ b/server/src/locale.cpp | |||
| @@ -69,6 +69,55 @@ public: | |||
| 69 | ~icu_priority_locale_vec_iterator() override = default; | 69 | ~icu_priority_locale_vec_iterator() override = default; |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | auto to_bcp47_lang_tag(std::locale locale) -> std::optional<std::string> | ||
| 73 | { | ||
| 74 | auto const& locale_info = std::use_facet<blocale::info>(locale); | ||
| 75 | auto ec = UErrorCode::U_ZERO_ERROR; | ||
| 76 | auto bcp47_lang_tag = | ||
| 77 | icu::Locale{locale_info.name().c_str()}.toLanguageTag<std::string>(ec); | ||
| 78 | if (U_FAILURE(ec)) | ||
| 79 | return std::nullopt; | ||
| 80 | return bcp47_lang_tag; | ||
| 81 | } | ||
| 82 | |||
| 83 | auto bcp47_tag::id = std::locale::id{}; | ||
| 84 | |||
| 85 | bcp47_tag::bcp47_tag(std::locale locale) | ||
| 86 | { | ||
| 87 | auto mtag = to_bcp47_lang_tag(locale); | ||
| 88 | if (!mtag) | ||
| 89 | throw std::invalid_argument{ | ||
| 90 | "locale name cannot be expressed as a BCP 47 language tag" | ||
| 91 | }; | ||
| 92 | tag_ = *mtag; | ||
| 93 | } | ||
| 94 | |||
| 95 | auto bcp47_tag::tag() const -> std::string_view { return tag_; } | ||
| 96 | |||
| 97 | auto selector::make_matcher(std::vector<std::locale> const& supported_locales) | ||
| 98 | -> icu::LocaleMatcher | ||
| 99 | { | ||
| 100 | auto builder = icu::LocaleMatcher::Builder{}; | ||
| 101 | for (auto const& supported_locale : supported_locales) | ||
| 102 | { | ||
| 103 | auto const& supported_locale_info = | ||
| 104 | std::use_facet<blocale::info>(supported_locale); | ||
| 105 | auto supported_icu_locale = | ||
| 106 | icu::Locale{supported_locale_info.name().c_str()}; | ||
| 107 | if (supported_icu_locale.isBogus()) | ||
| 108 | throw std::runtime_error{ | ||
| 109 | "supported locale gives rise to bogus ICU locale" | ||
| 110 | }; | ||
| 111 | builder.addSupportedLocale(supported_icu_locale); | ||
| 112 | } | ||
| 113 | builder.setNoDefaultLocale(); | ||
| 114 | auto ec = UErrorCode::U_ZERO_ERROR; | ||
| 115 | auto matcher = builder.build(ec); | ||
| 116 | if (U_FAILURE(ec)) | ||
| 117 | throw std::runtime_error{"failed to build icu::LocaleMatcher"}; | ||
| 118 | return matcher; | ||
| 119 | } | ||
| 120 | |||
| 72 | // Trimming optional whitespace as defined in RFC 9110, § 12.4.2. | 121 | // Trimming optional whitespace as defined in RFC 9110, § 12.4.2. |
| 73 | auto selector::ltrim_ows(std::string_view s) -> std::string_view | 122 | auto selector::ltrim_ows(std::string_view s) -> std::string_view |
| 74 | { | 123 | { |
| @@ -87,59 +136,6 @@ auto selector::trim_ows(std::string_view s) -> std::string_view | |||
| 87 | return rtrim_ows(ltrim_ows(s)); | 136 | return rtrim_ows(ltrim_ows(s)); |
| 88 | } | 137 | } |
| 89 | 138 | ||
| 90 | auto selector::from_icu_locale(icu::Locale const& l) const -> std::locale | ||
| 91 | { | ||
| 92 | auto posix_name = std::string{l.getLanguage()}; | ||
| 93 | if (l.getScript() && std::strlen(l.getScript()) > 0) | ||
| 94 | { | ||
| 95 | posix_name += "_"; | ||
| 96 | posix_name += l.getScript(); | ||
| 97 | } | ||
| 98 | if (l.getCountry() && std::strlen(l.getCountry()) > 0) | ||
| 99 | { | ||
| 100 | posix_name += "_"; | ||
| 101 | posix_name += l.getCountry(); | ||
| 102 | } | ||
| 103 | posix_name += ".UTF-8"; | ||
| 104 | auto added_at = false; | ||
| 105 | if (l.getVariant() && std::strlen(l.getVariant()) > 0) | ||
| 106 | { | ||
| 107 | added_at = true; | ||
| 108 | posix_name += "@"; | ||
| 109 | posix_name += l.getVariant(); | ||
| 110 | } | ||
| 111 | auto ec = UErrorCode::U_ZERO_ERROR; | ||
| 112 | auto* keywords = l.createKeywords(ec); | ||
| 113 | if (U_FAILURE(ec)) | ||
| 114 | throw std::runtime_error{"failed to create keywords"}; | ||
| 115 | if (keywords) | ||
| 116 | { | ||
| 117 | std::int32_t kw_len = 0; | ||
| 118 | char const* kw = nullptr; | ||
| 119 | while (kw = keywords->next(&kw_len, ec), !U_FAILURE(ec) && kw) | ||
| 120 | { | ||
| 121 | auto value = | ||
| 122 | l.getKeywordValue<std::string>(icu::StringPiece(kw, kw_len), ec); | ||
| 123 | if (!added_at) | ||
| 124 | { | ||
| 125 | posix_name += "@"; | ||
| 126 | added_at = true; | ||
| 127 | } | ||
| 128 | else | ||
| 129 | { | ||
| 130 | posix_name += ";"; | ||
| 131 | } | ||
| 132 | posix_name += kw; | ||
| 133 | posix_name += "="; | ||
| 134 | posix_name += value; | ||
| 135 | } | ||
| 136 | if (U_FAILURE(ec)) | ||
| 137 | throw std::runtime_error{"failed to iterate over keywords"}; | ||
| 138 | delete keywords; | ||
| 139 | } | ||
| 140 | return lgen_->generate(posix_name); | ||
| 141 | } | ||
| 142 | |||
| 143 | auto selector::select(std::string_view accept_language) const -> std::locale | 139 | auto selector::select(std::string_view accept_language) const -> std::locale |
| 144 | { | 140 | { |
| 145 | using namespace std::literals::string_view_literals; | 141 | using namespace std::literals::string_view_literals; |
| @@ -194,21 +190,10 @@ auto selector::select(std::string_view accept_language) const -> std::locale | |||
| 194 | auto res = matcher_.getBestMatchResult(it, ec); | 190 | auto res = matcher_.getBestMatchResult(it, ec); |
| 195 | if (U_FAILURE(ec)) | 191 | if (U_FAILURE(ec)) |
| 196 | return default_; | 192 | return default_; |
| 197 | auto resolved = res.makeResolvedLocale(ec); // TODO: maybe don't? | 193 | auto i = res.getSupportedIndex(); |
| 198 | if (U_FAILURE(ec)) | 194 | if (i == -1) |
| 199 | return from_icu_locale(*res.getSupportedLocale()); | 195 | return default_; |
| 200 | return from_icu_locale(resolved); | 196 | return supported_[util::size_from_int(i).value()]; |
| 201 | } | ||
| 202 | |||
| 203 | auto to_bcp47_lang_tag(std::locale locale) -> std::optional<std::string> | ||
| 204 | { | ||
| 205 | auto const& locale_info = std::use_facet<blocale::info>(locale); | ||
| 206 | auto ec = UErrorCode::U_ZERO_ERROR; | ||
| 207 | auto bcp47_lang_tag = | ||
| 208 | icu::Locale{locale_info.name().c_str()}.toLanguageTag<std::string>(ec); | ||
| 209 | if (U_FAILURE(ec)) | ||
| 210 | return std::nullopt; | ||
| 211 | return bcp47_lang_tag; | ||
| 212 | } | 197 | } |
| 213 | 198 | ||
| 214 | #ifdef LOCALEDIR | 199 | #ifdef LOCALEDIR |
diff --git a/server/src/locale.cppm b/server/src/locale.cppm index 263de1d..00a3d36 100644 --- a/server/src/locale.cppm +++ b/server/src/locale.cppm | |||
| @@ -26,65 +26,52 @@ concept locale_input_range = | |||
| 26 | && std:: | 26 | && std:: |
| 27 | same_as<std::locale const&, std::ranges::range_const_reference_t<T>>; | 27 | same_as<std::locale const&, std::ranges::range_const_reference_t<T>>; |
| 28 | 28 | ||
| 29 | class bcp47_tag : public std::locale::facet | ||
| 30 | { | ||
| 31 | std::string tag_; | ||
| 32 | |||
| 33 | public: | ||
| 34 | static std::locale::id id; | ||
| 35 | |||
| 36 | explicit bcp47_tag(std::locale locale); | ||
| 37 | ~bcp47_tag() override = default; | ||
| 38 | |||
| 39 | auto tag() const -> std::string_view; | ||
| 40 | }; | ||
| 41 | |||
| 29 | // Helps select a locale based on the Accept-Language header in an | 42 | // Helps select a locale based on the Accept-Language header in an |
| 30 | // HTTP request. | 43 | // HTTP request. |
| 31 | export class selector | 44 | export class selector |
| 32 | { | 45 | { |
| 46 | std::vector<std::locale> supported_; | ||
| 33 | std::locale default_; | 47 | std::locale default_; |
| 34 | icu::LocaleMatcher matcher_; | 48 | icu::LocaleMatcher matcher_; |
| 35 | std::shared_ptr<blocale::generator const> lgen_; | ||
| 36 | 49 | ||
| 37 | auto make_matcher( | 50 | static auto make_matcher(std::vector<std::locale> const& supported_locales) |
| 38 | locale_input_range auto supported_locales, std::locale default_locale) | 51 | -> icu::LocaleMatcher; |
| 39 | { | ||
| 40 | auto builder = icu::LocaleMatcher::Builder{}; | ||
| 41 | for (auto const& supported_locale : supported_locales) | ||
| 42 | { | ||
| 43 | auto const& supported_locale_info = | ||
| 44 | std::use_facet<blocale::info>(supported_locale); | ||
| 45 | auto supported_icu_locale = | ||
| 46 | icu::Locale{supported_locale_info.name().c_str()}; | ||
| 47 | if (supported_icu_locale.isBogus()) | ||
| 48 | throw std::runtime_error{ | ||
| 49 | "supported locale gives rise to bogus ICU locale" | ||
| 50 | }; | ||
| 51 | builder.addSupportedLocale(supported_icu_locale); | ||
| 52 | } | ||
| 53 | auto const& default_locale_info = | ||
| 54 | std::use_facet<blocale::info>(default_locale); | ||
| 55 | auto default_icu_locale = icu::Locale{default_locale_info.name().c_str()}; | ||
| 56 | if (default_icu_locale.isBogus()) | ||
| 57 | throw std::runtime_error{"default locale gives rise to bogus ICU locale"}; | ||
| 58 | builder.setDefaultLocale(&default_icu_locale); | ||
| 59 | auto ec = UErrorCode::U_ZERO_ERROR; | ||
| 60 | auto matcher = builder.build(ec); | ||
| 61 | if (U_FAILURE(ec)) | ||
| 62 | throw std::runtime_error{"failed to build icu::LocaleMatcher"}; | ||
| 63 | return matcher; | ||
| 64 | } | ||
| 65 | 52 | ||
| 66 | // Trimming optional whitespace as defined in RFC 9110, § 12.4.2. | 53 | // Trimming optional whitespace as defined in RFC 9110, § 12.4.2. |
| 67 | static auto ltrim_ows(std::string_view s) -> std::string_view; | 54 | static auto ltrim_ows(std::string_view s) -> std::string_view; |
| 68 | static auto rtrim_ows(std::string_view s) -> std::string_view; | 55 | static auto rtrim_ows(std::string_view s) -> std::string_view; |
| 69 | static auto trim_ows(std::string_view s) -> std::string_view; | 56 | static auto trim_ows(std::string_view s) -> std::string_view; |
| 70 | 57 | ||
| 71 | auto from_icu_locale(icu::Locale const& l) const -> std::locale; | ||
| 72 | |||
| 73 | public: | 58 | public: |
| 74 | // Note: lgen must live at least as long as the selector constructed here! | 59 | explicit selector(locale_input_range auto supported, std::locale default_) |
| 75 | // It is unfortunately not possible to copy/move a blocale::generator. | 60 | : supported_{ |
| 76 | explicit selector( | 61 | std::from_range, |
| 77 | locale_input_range auto locales, std::locale default_, | 62 | supported |
| 78 | std::shared_ptr<blocale::generator const> lgen) | 63 | | std::views::transform( |
| 79 | : default_{default_}, matcher_{make_matcher(locales, default_)}, lgen_{lgen} | 64 | [](std::locale const& locale) -> std::locale |
| 65 | { return std::locale{locale, new bcp47_tag{locale}}; }) | ||
| 66 | }, | ||
| 67 | default_{std::locale{default_, new bcp47_tag{default_}}}, | ||
| 68 | matcher_{make_matcher(supported_)} | ||
| 80 | { | 69 | { |
| 81 | } | 70 | } |
| 82 | 71 | ||
| 83 | auto select(std::string_view accept_language) const -> std::locale; | 72 | auto select(std::string_view accept_language) const -> std::locale; |
| 84 | }; | 73 | }; |
| 85 | 74 | ||
| 86 | auto to_bcp47_lang_tag(std::locale locale) -> std::optional<std::string>; | ||
| 87 | |||
| 88 | export auto make_generator() -> std::shared_ptr<blocale::generator const>; | 75 | export auto make_generator() -> std::shared_ptr<blocale::generator const>; |
| 89 | 76 | ||
| 90 | } // namespace routemon::locale | 77 | } // namespace routemon::locale |
diff --git a/server/src/main.cpp b/server/src/main.cpp index 99f19da..3ef45c6 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp | |||
| @@ -33,10 +33,9 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 33 | auto locales = { | 33 | auto locales = { |
| 34 | default_locale, | 34 | default_locale, |
| 35 | lgen->generate("nl_NL.UTF-8"), | 35 | lgen->generate("nl_NL.UTF-8"), |
| 36 | lgen->generate("de_DE.UTF-8"), | ||
| 37 | lgen->generate("en_GB.UTF-8"), | 36 | lgen->generate("en_GB.UTF-8"), |
| 38 | }; | 37 | }; |
| 39 | auto lsel = routemon::locale::selector{locales, default_locale, lgen}; | 38 | auto lsel = routemon::locale::selector{locales, default_locale}; |
| 40 | 39 | ||
| 41 | auto ioc = net::io_context{1 /* concurrency hint */}; | 40 | auto ioc = net::io_context{1 /* concurrency hint */}; |
| 42 | 41 | ||
diff --git a/server/src/srv.cpp b/server/src/srv.cpp index f79da33..adfef18 100644 --- a/server/src/srv.cpp +++ b/server/src/srv.cpp | |||
| @@ -51,12 +51,43 @@ auto gpx_parse_error() noexcept -> std::error_code | |||
| 51 | 51 | ||
| 52 | class gpx_parse_result | 52 | class gpx_parse_result |
| 53 | { | 53 | { |
| 54 | chrono::steady_clock::duration init_parse_dur_; | ||
| 55 | chrono::steady_clock::duration put_parse_dur_; | ||
| 56 | chrono::steady_clock::duration finish_parse_dur_; | ||
| 54 | std::variant<std::exception_ptr, gpx::file> res_; | 57 | std::variant<std::exception_ptr, gpx::file> res_; |
| 55 | 58 | ||
| 56 | public: | 59 | public: |
| 57 | auto set_exception(std::exception_ptr ex) noexcept { res_ = ex; } | 60 | auto set_exception(std::exception_ptr ex) noexcept { res_ = ex; } |
| 58 | auto set_gpx_file(gpx::file&& f) noexcept { res_ = std::move(f); } | 61 | auto set_gpx_file(gpx::file&& f) noexcept { res_ = std::move(f); } |
| 59 | 62 | ||
| 63 | auto init_parse_duration(chrono::steady_clock::duration dur) noexcept | ||
| 64 | { | ||
| 65 | init_parse_dur_ = dur; | ||
| 66 | } | ||
| 67 | [[nodiscard]] auto init_parse_duration() const noexcept | ||
| 68 | -> chrono::steady_clock::duration | ||
| 69 | { | ||
| 70 | return init_parse_dur_; | ||
| 71 | } | ||
| 72 | auto put_parse_duration(chrono::steady_clock::duration dur) noexcept | ||
| 73 | { | ||
| 74 | put_parse_dur_ = dur; | ||
| 75 | } | ||
| 76 | [[nodiscard]] auto put_parse_duration() const noexcept | ||
| 77 | -> chrono::steady_clock::duration | ||
| 78 | { | ||
| 79 | return put_parse_dur_; | ||
| 80 | } | ||
| 81 | auto finish_parse_duration(chrono::steady_clock::duration dur) noexcept | ||
| 82 | { | ||
| 83 | finish_parse_dur_ = dur; | ||
| 84 | } | ||
| 85 | [[nodiscard]] auto finish_parse_duration() const noexcept | ||
| 86 | -> chrono::steady_clock::duration | ||
| 87 | { | ||
| 88 | return finish_parse_dur_; | ||
| 89 | } | ||
| 90 | |||
| 60 | auto unwrap() -> gpx::file&& | 91 | auto unwrap() -> gpx::file&& |
| 61 | { | 92 | { |
| 62 | return std::visit( | 93 | return std::visit( |
| @@ -82,11 +113,15 @@ struct readable_gpx_body | |||
| 82 | { | 113 | { |
| 83 | gpx::reader r_; | 114 | gpx::reader r_; |
| 84 | util::not_null<value_type*> res_; | 115 | util::not_null<value_type*> res_; |
| 116 | std::inplace_vector<char, 4096> buf_; | ||
| 85 | 117 | ||
| 86 | public: | 118 | public: |
| 87 | template <bool isRequest, bhttp::concepts::fields Fields> | 119 | template <bool isRequest, bhttp::concepts::fields Fields> |
| 88 | explicit reader(bhttp::header<isRequest, Fields>&, value_type& v) : res_{&v} | 120 | explicit reader(bhttp::header<isRequest, Fields>&, value_type& v) : res_{&v} |
| 89 | { | 121 | { |
| 122 | res_->init_parse_duration({}); | ||
| 123 | res_->put_parse_duration({}); | ||
| 124 | res_->finish_parse_duration({}); | ||
| 90 | } | 125 | } |
| 91 | 126 | ||
| 92 | // The following methods (which are called by Beast) are marked | 127 | // The following methods (which are called by Beast) are marked |
| @@ -99,6 +134,7 @@ struct readable_gpx_body | |||
| 99 | init(boost::optional<std::uint64_t> /* n */, beast::error_code& ec) noexcept | 134 | init(boost::optional<std::uint64_t> /* n */, beast::error_code& ec) noexcept |
| 100 | -> void | 135 | -> void |
| 101 | { | 136 | { |
| 137 | auto const init_start = chrono::steady_clock::now(); | ||
| 102 | try | 138 | try |
| 103 | { | 139 | { |
| 104 | r_.init(); | 140 | r_.init(); |
| @@ -109,23 +145,38 @@ struct readable_gpx_body | |||
| 109 | res_->set_exception(std::current_exception()); | 145 | res_->set_exception(std::current_exception()); |
| 110 | ec = gpx_parse_error(); | 146 | ec = gpx_parse_error(); |
| 111 | } | 147 | } |
| 148 | res_->init_parse_duration(chrono::steady_clock::now() - init_start); | ||
| 112 | } | 149 | } |
| 113 | 150 | ||
| 114 | auto | 151 | auto |
| 115 | put(beast::concepts::const_buffer_sequence auto b, | 152 | put(beast::concepts::const_buffer_sequence auto b, |
| 116 | beast::error_code& ec) noexcept -> std::size_t | 153 | beast::error_code& ec) noexcept -> std::size_t |
| 117 | { | 154 | { |
| 155 | auto const put_start = chrono::steady_clock::now(); | ||
| 118 | auto total = 0uz; | 156 | auto total = 0uz; |
| 119 | try | 157 | try |
| 120 | { | 158 | { |
| 121 | for (auto it = net::buffer_sequence_begin(b); | 159 | for (auto it = net::buffer_sequence_begin(b); |
| 122 | it != net::buffer_sequence_end(b); it++) | 160 | it != net::buffer_sequence_end(b); it++) |
| 123 | { | 161 | { |
| 124 | r_.put( | 162 | auto cur_in_buf = net::const_buffer{*it}; |
| 125 | std::string_view{ | 163 | while (cur_in_buf.size() > 0) |
| 126 | static_cast<char const*>(it->data()), it->size() | 164 | { |
| 127 | }); | 165 | auto to_read = |
| 128 | total += it->size(); | 166 | std::min(cur_in_buf.size(), buf_.max_size() - buf_.size()); |
| 167 | buf_.append_range( | ||
| 168 | std::span{ | ||
| 169 | static_cast<char const*>(cur_in_buf.data()), to_read | ||
| 170 | }); | ||
| 171 | cur_in_buf += to_read; | ||
| 172 | total += to_read; | ||
| 173 | |||
| 174 | if (buf_.size() == buf_.max_size()) | ||
| 175 | { | ||
| 176 | r_.put(std::string_view{buf_}); | ||
| 177 | buf_.clear(); | ||
| 178 | } | ||
| 179 | } | ||
| 129 | } | 180 | } |
| 130 | ec = {}; | 181 | ec = {}; |
| 131 | } | 182 | } |
| @@ -134,13 +185,23 @@ struct readable_gpx_body | |||
| 134 | res_->set_exception(std::current_exception()); | 185 | res_->set_exception(std::current_exception()); |
| 135 | ec = gpx_parse_error(); | 186 | ec = gpx_parse_error(); |
| 136 | } | 187 | } |
| 188 | res_->put_parse_duration( | ||
| 189 | res_->put_parse_duration() | ||
| 190 | + (chrono::steady_clock::now() - put_start)); | ||
| 137 | return total; | 191 | return total; |
| 138 | } | 192 | } |
| 139 | 193 | ||
| 140 | auto finish(beast::error_code& ec) noexcept | 194 | auto finish(beast::error_code& ec) noexcept |
| 141 | { | 195 | { |
| 196 | auto const finish_start = chrono::steady_clock::now(); | ||
| 142 | try | 197 | try |
| 143 | { | 198 | { |
| 199 | if (buf_.size() > 0) | ||
| 200 | { | ||
| 201 | r_.put(std::string_view{buf_}); | ||
| 202 | buf_.clear(); | ||
| 203 | } | ||
| 204 | |||
| 144 | res_->set_gpx_file(r_.finish()); | 205 | res_->set_gpx_file(r_.finish()); |
| 145 | ec = {}; | 206 | ec = {}; |
| 146 | } | 207 | } |
| @@ -149,6 +210,7 @@ struct readable_gpx_body | |||
| 149 | res_->set_exception(std::current_exception()); | 210 | res_->set_exception(std::current_exception()); |
| 150 | ec = gpx_parse_error(); | 211 | ec = gpx_parse_error(); |
| 151 | } | 212 | } |
| 213 | res_->finish_parse_duration(chrono::steady_clock::now() - finish_start); | ||
| 152 | } | 214 | } |
| 153 | }; | 215 | }; |
| 154 | }; | 216 | }; |
| @@ -168,12 +230,18 @@ auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) | |||
| 168 | auto const before_read_gpx = chrono::steady_clock::now(); | 230 | auto const before_read_gpx = chrono::steady_clock::now(); |
| 169 | auto req = | 231 | auto req = |
| 170 | co_await http::read_request<readable_gpx_body>(ctx, std::move(r)); | 232 | co_await http::read_request<readable_gpx_body>(ctx, std::move(r)); |
| 171 | gpx_file = std::move(req->body().unwrap()); | ||
| 172 | l.debug( | 233 | l.debug( |
| 173 | "Read GPX request body in {}", | 234 | "Read GPX request body in {}; init took {}, actual parsing {}, " |
| 235 | "finishing {}", | ||
| 174 | chrono::duration<double, std::milli>{ | 236 | chrono::duration<double, std::milli>{ |
| 175 | chrono::steady_clock::now() - before_read_gpx | 237 | chrono::steady_clock::now() - before_read_gpx |
| 238 | }, | ||
| 239 | chrono::duration<double, std::milli>{req->body().init_parse_duration()}, | ||
| 240 | chrono::duration<double, std::milli>{req->body().put_parse_duration()}, | ||
| 241 | chrono::duration<double, std::milli>{ | ||
| 242 | req->body().finish_parse_duration() | ||
| 176 | }); | 243 | }); |
| 244 | gpx_file = std::move(req->body().unwrap()); | ||
| 177 | } | 245 | } |
| 178 | catch (std::exception& ex) | 246 | catch (std::exception& ex) |
| 179 | { | 247 | { |
| @@ -251,8 +319,10 @@ auto server::make_global_middleware(config::http_server const& cfg) | |||
| 251 | { | 319 | { |
| 252 | return http:: | 320 | return http:: |
| 253 | middleware_compose<http::base_ctx, http::base_ctx, http::base_ctx>( | 321 | middleware_compose<http::base_ctx, http::base_ctx, http::base_ctx>( |
| 254 | http::trace_id_middleware<http::base_ctx>, | 322 | http::expose_content_language_middleware<http::base_ctx>, |
| 255 | http::cors_middleware<http::base_ctx>(cfg.allow_origins)); | 323 | middleware_compose<http::base_ctx, http::base_ctx, http::base_ctx>( |
| 324 | http::expose_trace_id_middleware<http::base_ctx>, | ||
| 325 | http::cors_middleware<http::base_ctx>(cfg.allow_origins))); | ||
| 256 | } | 326 | } |
| 257 | 327 | ||
| 258 | server::server( | 328 | server::server( |