diff --git a/include/glaze/rpc/repe/repe.hpp b/include/glaze/rpc/repe/repe.hpp index 25c31f649b..0c0cb7ae03 100644 --- a/include/glaze/rpc/repe/repe.hpp +++ b/include/glaze/rpc/repe/repe.hpp @@ -554,7 +554,17 @@ namespace glz::repe } auto start = b; - glz::parse::template op(std::forward(value), ctx, b, e); + // The body is a view into the request span, so nothing is guaranteed to follow it. + // A null_terminated read drops its end checks and scans for the '\0' sentinel that + // only a std::string body would supply, so read the view as unterminated input and + // clear the end_reached a value finishing at the span end reports, matching what + // finalize_read_context does for glz::read. + static constexpr auto BodyOpts = opt_false; + + glz::parse::template op(std::forward(value), ctx, b, e); + if (ctx.error == error_code::end_reached && ctx.depth == 0) { + ctx.error = error_code::none; + } if (bool(ctx.error)) { error_ctx ec{size_t(b - start), ctx.error, ctx.custom_error_message}; diff --git a/tests/networking_tests/registry_view_test/registry_view_test.cpp b/tests/networking_tests/registry_view_test/registry_view_test.cpp index 536591a05c..b39237601f 100644 --- a/tests/networking_tests/registry_view_test/registry_view_test.cpp +++ b/tests/networking_tests/registry_view_test/registry_view_test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "glaze/rpc/registry.hpp" #include "ut/ut.hpp" @@ -289,4 +290,53 @@ suite registry_span_call_tests = [] { }; }; +struct vec2 +{ + int x{}; + int y{}; +}; + +struct object_api +{ + vec2 position{}; +}; + +suite span_call_unterminated_buffer_tests = [] { + // make_request hands back a std::string, whose data()[size()] is a '\0' the reader + // can stop on. Off the wire the body ends where the buffer ends, so copy the message + // into an exactly sized vector to reproduce that. + auto exact_buffer = [](std::string_view message) { return std::vector(message.begin(), message.end()); }; + + "scalar_body_ending_at_buffer_end"_test = [&] { + glz::registry<> registry; + test_api api{}; + registry.on(api); + + const auto request = exact_buffer(make_request("/value", "7")); + std::string response_buf; + registry.call(std::span{request.data(), request.size()}, response_buf); + + expect(api.value == 7) << "Scalar body should be applied"; + auto result = glz::repe::parse_request({response_buf.data(), response_buf.size()}); + expect(bool(result)) << "Response should be parseable"; + expect(result.request.error() == glz::error_code::none) << "No error expected"; + }; + + "object_body_ending_at_buffer_end"_test = [&] { + glz::registry<> registry; + object_api api{}; + registry.on(api); + + const auto request = exact_buffer(make_request("/position", R"({"x":3,"y":4})")); + std::string response_buf; + registry.call(std::span{request.data(), request.size()}, response_buf); + + expect(api.position.x == 3) << "Object body should be applied"; + expect(api.position.y == 4) << "Object body should be applied"; + auto result = glz::repe::parse_request({response_buf.data(), response_buf.size()}); + expect(bool(result)) << "Response should be parseable"; + expect(result.request.error() == glz::error_code::none) << "No error expected"; + }; +}; + int main() { return 0; }