diff options
Diffstat (limited to 'server/src/srv.cpp')
| -rw-r--r-- | server/src/srv.cpp | 141 |
1 files changed, 131 insertions, 10 deletions
diff --git a/server/src/srv.cpp b/server/src/srv.cpp index adfef18..a901f8e 100644 --- a/server/src/srv.cpp +++ b/server/src/srv.cpp | |||
| @@ -6,6 +6,7 @@ module; | |||
| 6 | #include <boost/asio/ip/tcp.hpp> | 6 | #include <boost/asio/ip/tcp.hpp> |
| 7 | #include <boost/beast/core.hpp> | 7 | #include <boost/beast/core.hpp> |
| 8 | #include <boost/beast/http.hpp> | 8 | #include <boost/beast/http.hpp> |
| 9 | #include <boost/beast/zlib.hpp> | ||
| 9 | #include <boost/config.hpp> | 10 | #include <boost/config.hpp> |
| 10 | #include <boost/json.hpp> | 11 | #include <boost/json.hpp> |
| 11 | #include <boost/locale/generator.hpp> | 12 | #include <boost/locale/generator.hpp> |
| @@ -156,10 +157,8 @@ struct readable_gpx_body | |||
| 156 | auto total = 0uz; | 157 | auto total = 0uz; |
| 157 | try | 158 | try |
| 158 | { | 159 | { |
| 159 | for (auto it = net::buffer_sequence_begin(b); | 160 | for (auto cur_in_buf : beast::buffers_range(b)) |
| 160 | it != net::buffer_sequence_end(b); it++) | ||
| 161 | { | 161 | { |
| 162 | auto cur_in_buf = net::const_buffer{*it}; | ||
| 163 | while (cur_in_buf.size() > 0) | 162 | while (cur_in_buf.size() > 0) |
| 164 | { | 163 | { |
| 165 | auto to_read = | 164 | auto to_read = |
| @@ -217,6 +216,120 @@ struct readable_gpx_body | |||
| 217 | static_assert(bhttp::concepts::body<readable_gpx_body>); | 216 | static_assert(bhttp::concepts::body<readable_gpx_body>); |
| 218 | static_assert(bhttp::concepts::body_reader<readable_gpx_body>); | 217 | static_assert(bhttp::concepts::body_reader<readable_gpx_body>); |
| 219 | 218 | ||
| 219 | template <bhttp::concepts::body_reader Inner> | ||
| 220 | struct raw_deflate_body | ||
| 221 | { | ||
| 222 | using value_type = Inner::value_type; | ||
| 223 | |||
| 224 | class reader | ||
| 225 | { | ||
| 226 | static constexpr auto const chunk_size = 16384; | ||
| 227 | |||
| 228 | Inner::reader r_; | ||
| 229 | bool stream_ended_ = false; | ||
| 230 | beast::zlib::z_params zs_ = {}; | ||
| 231 | beast::zlib::inflate_stream inflate_strm_ = {}; | ||
| 232 | std::unique_ptr<std::array<char, chunk_size>> in_buf_ = nullptr; | ||
| 233 | std::unique_ptr<std::array<char, chunk_size>> out_buf_ = nullptr; | ||
| 234 | |||
| 235 | auto inflate_chunk(beast::error_code& ec) -> void | ||
| 236 | { | ||
| 237 | assert(zs_.avail_in > 0); | ||
| 238 | zs_.next_in = in_buf_->data(); | ||
| 239 | |||
| 240 | do | ||
| 241 | { | ||
| 242 | zs_.avail_out = out_buf_->size(); | ||
| 243 | zs_.next_out = out_buf_->data(); | ||
| 244 | |||
| 245 | inflate_strm_.write(zs_, beast::zlib::Flush::none, ec); | ||
| 246 | assert(ec != beast::zlib::error::stream_error); | ||
| 247 | if (ec == beast::zlib::error::need_buffers) | ||
| 248 | ec = {}; | ||
| 249 | if (ec == beast::zlib::error::end_of_stream) | ||
| 250 | { | ||
| 251 | ec = {}; | ||
| 252 | stream_ended_ = true; | ||
| 253 | } | ||
| 254 | if (ec) | ||
| 255 | break; | ||
| 256 | |||
| 257 | auto have = chunk_size - zs_.avail_out; | ||
| 258 | auto cbuf = | ||
| 259 | net::buffer(static_cast<void const*>(out_buf_->data()), have); | ||
| 260 | |||
| 261 | auto inner_ec = beast::error_code{}; | ||
| 262 | do | ||
| 263 | cbuf += r_.put(cbuf, inner_ec); | ||
| 264 | while (cbuf.size() > 0 && !inner_ec); | ||
| 265 | if (inner_ec) | ||
| 266 | { | ||
| 267 | ec = inner_ec; | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | } | ||
| 271 | while (zs_.avail_out == 0); | ||
| 272 | |||
| 273 | // In the last case (stream_ended_), the user has sent too much data | ||
| 274 | assert(zs_.avail_in == 0 || ec || stream_ended_); | ||
| 275 | } | ||
| 276 | |||
| 277 | public: | ||
| 278 | template <bool isRequest, bhttp::concepts::fields Fields> | ||
| 279 | explicit reader(bhttp::header<isRequest, Fields>& hdr, value_type& v) | ||
| 280 | : r_{hdr, v} | ||
| 281 | { | ||
| 282 | } | ||
| 283 | |||
| 284 | auto | ||
| 285 | init(boost::optional<std::uint64_t> /* n */, beast::error_code& ec) noexcept | ||
| 286 | -> void | ||
| 287 | { | ||
| 288 | in_buf_.reset(new std::array<char, chunk_size>{}); | ||
| 289 | out_buf_.reset(new std::array<char, chunk_size>{}); | ||
| 290 | ec = {}; | ||
| 291 | } | ||
| 292 | |||
| 293 | auto | ||
| 294 | put(beast::concepts::const_buffer_sequence auto b, | ||
| 295 | beast::error_code& ec) noexcept -> std::size_t | ||
| 296 | { | ||
| 297 | auto in_buf = net::buffer(in_buf_->data(), in_buf_->size()); | ||
| 298 | in_buf += zs_.avail_in; | ||
| 299 | |||
| 300 | auto consumed_from_b = net::buffer_copy(in_buf, b); | ||
| 301 | zs_.avail_in += consumed_from_b; | ||
| 302 | |||
| 303 | if (zs_.avail_in == in_buf_->size()) | ||
| 304 | inflate_chunk(ec); | ||
| 305 | else | ||
| 306 | ec = {}; | ||
| 307 | |||
| 308 | return consumed_from_b; | ||
| 309 | } | ||
| 310 | |||
| 311 | auto finish(beast::error_code& ec) noexcept | ||
| 312 | { | ||
| 313 | ec = {}; | ||
| 314 | if (zs_.avail_in > 0) | ||
| 315 | inflate_chunk(ec); | ||
| 316 | if (!ec && !stream_ended_) | ||
| 317 | ec = beast::zlib::error::stream_error; | ||
| 318 | if (stream_ended_ && zs_.avail_in > 0) | ||
| 319 | // client sent data past deflate stream end | ||
| 320 | ec = beast::zlib::error::stream_error; | ||
| 321 | |||
| 322 | in_buf_.reset(); | ||
| 323 | out_buf_.reset(); | ||
| 324 | inflate_strm_.reset(); | ||
| 325 | if (!ec) | ||
| 326 | { | ||
| 327 | r_.finish(ec); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | }; | ||
| 331 | }; | ||
| 332 | |||
| 220 | auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) | 333 | auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) |
| 221 | -> net::awaitable<http::presponse> | 334 | -> net::awaitable<http::presponse> |
| 222 | { | 335 | { |
| @@ -228,23 +341,31 @@ auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) | |||
| 228 | { | 341 | { |
| 229 | l.debug("Reading GPX request body"); | 342 | l.debug("Reading GPX request body"); |
| 230 | auto const before_read_gpx = chrono::steady_clock::now(); | 343 | auto const before_read_gpx = chrono::steady_clock::now(); |
| 231 | auto req = | 344 | auto parse_res = |
| 232 | co_await http::read_request<readable_gpx_body>(ctx, std::move(r)); | 345 | r.p->get()[bhttp::field::content_encoding] == "x-deflate-raw" |
| 346 | ? std::move( | ||
| 347 | (co_await http::read_request<raw_deflate_body<readable_gpx_body>>( | ||
| 348 | ctx, std::move(r))) | ||
| 349 | .body()) | ||
| 350 | : std::move((co_await http::read_request<readable_gpx_body>( | ||
| 351 | ctx, std::move(r))) | ||
| 352 | .body()); | ||
| 233 | l.debug( | 353 | l.debug( |
| 234 | "Read GPX request body in {}; init took {}, actual parsing {}, " | 354 | "Read GPX request body in {}; init took {}, actual parsing {}, " |
| 235 | "finishing {}", | 355 | "finishing {}", |
| 236 | chrono::duration<double, std::milli>{ | 356 | chrono::duration<double, std::milli>{ |
| 237 | chrono::steady_clock::now() - before_read_gpx | 357 | chrono::steady_clock::now() - before_read_gpx |
| 238 | }, | 358 | }, |
| 239 | chrono::duration<double, std::milli>{req->body().init_parse_duration()}, | 359 | chrono::duration<double, std::milli>{parse_res.init_parse_duration()}, |
| 240 | chrono::duration<double, std::milli>{req->body().put_parse_duration()}, | 360 | chrono::duration<double, std::milli>{parse_res.put_parse_duration()}, |
| 241 | chrono::duration<double, std::milli>{ | 361 | chrono::duration<double, std::milli>{ |
| 242 | req->body().finish_parse_duration() | 362 | parse_res.finish_parse_duration() |
| 243 | }); | 363 | }); |
| 244 | gpx_file = std::move(req->body().unwrap()); | 364 | gpx_file = std::move(parse_res.unwrap()); |
| 245 | } | 365 | } |
| 246 | catch (std::exception& ex) | 366 | catch (std::exception& ex) |
| 247 | { | 367 | { |
| 368 | l_.warn("Failed to parse GPX file: {}", ex.what()); | ||
| 248 | // TODO: more detailed problem reporting | 369 | // TODO: more detailed problem reporting |
| 249 | auto tpl = problem::tpl{ | 370 | auto tpl = problem::tpl{ |
| 250 | .status = bhttp::status::bad_request, | 371 | .status = bhttp::status::bad_request, |
| @@ -280,7 +401,7 @@ auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) | |||
| 280 | auto handler::handle_sysinfo(l0_ctx ctx, http::readable_request r) | 401 | auto handler::handle_sysinfo(l0_ctx ctx, http::readable_request r) |
| 281 | -> net::awaitable<http::presponse> | 402 | -> net::awaitable<http::presponse> |
| 282 | { | 403 | { |
| 283 | auto req = co_await http::read_request<bhttp::empty_body>(ctx, std::move(r)); | 404 | auto req = co_await http::read_empty_request(ctx, std::move(r)); |
| 284 | auto info = inner_.sysinfo(); | 405 | auto info = inner_.sysinfo(); |
| 285 | 406 | ||
| 286 | auto rsp = http::make_rsp<bhttp::string_body>( | 407 | auto rsp = http::make_rsp<bhttp::string_body>( |