summaryrefslogtreecommitdiffstats
path: root/server/src/srv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/srv.cpp')
-rw-r--r--server/src/srv.cpp141
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
217static_assert(bhttp::concepts::body<readable_gpx_body>); 216static_assert(bhttp::concepts::body<readable_gpx_body>);
218static_assert(bhttp::concepts::body_reader<readable_gpx_body>); 217static_assert(bhttp::concepts::body_reader<readable_gpx_body>);
219 218
219template <bhttp::concepts::body_reader Inner>
220struct 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
220auto handler::handle_process_gpx(l0_ctx ctx, http::readable_request r) 333auto 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)
280auto handler::handle_sysinfo(l0_ctx ctx, http::readable_request r) 401auto 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>(