From e8a4336ab8a8cfb85938c6ca611c5ef3f2b6dc03 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Fri, 29 May 2026 15:59:35 +0200 Subject: [PATCH 1/4] Add test-case for `SendFile()` with non-existant paths --- test/remote-httpmessage.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/remote-httpmessage.cpp b/test/remote-httpmessage.cpp index acbe612c7..23e3e0823 100644 --- a/test/remote-httpmessage.cpp +++ b/test/remote-httpmessage.cpp @@ -353,4 +353,19 @@ BOOST_AUTO_TEST_CASE(response_sendfile) BOOST_REQUIRE_EQUAL(ss.str(), parser.get().body()); } +BOOST_AUTO_TEST_CASE(response_sendfile_invalid_path) +{ + auto future = SpawnSynchronizedCoroutine([this](boost::asio::yield_context yc) { + HttpApiResponse response(server); + + response.result(http::status::ok); + BOOST_REQUIRE_THROW(response.SendFile("", yc), std::ios_base::failure); + }); + + auto status = future.wait_for(10s); + if (status != std::future_status::ready) { + BOOST_FAIL("Exception not thrown."); + } +} + BOOST_AUTO_TEST_SUITE_END() From 281dd133c7998d803af35aee092c8e96ecb90283 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Fri, 29 May 2026 13:51:12 +0200 Subject: [PATCH 2/4] Fix handling the `std::ifstream::failbit` in `OutgoingHttpMessage` --- lib/remote/httpmessage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote/httpmessage.cpp b/lib/remote/httpmessage.cpp index 221c8945e..be5ceee4b 100644 --- a/lib/remote/httpmessage.cpp +++ b/lib/remote/httpmessage.cpp @@ -226,7 +226,7 @@ void OutgoingHttpMessage::SendFile( ) { std::ifstream fp(path.CStr(), std::ifstream::in | std::ifstream::binary | std::ifstream::ate); - fp.exceptions(std::ifstream::badbit | std::ifstream::eofbit); + fp.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); std::uint64_t remaining = fp.tellg(); fp.seekg(0); From 6a3979fd1fe5fc82508135a49e5ddb62b3b69e63 Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Mon, 1 Jun 2026 16:55:32 +0200 Subject: [PATCH 3/4] Fix handling `std::ifstream` exceptions in `ConfigFilesHandler` --- lib/remote/configfileshandler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/remote/configfileshandler.cpp b/lib/remote/configfileshandler.cpp index 28ea8be75..7f33e067c 100644 --- a/lib/remote/configfileshandler.cpp +++ b/lib/remote/configfileshandler.cpp @@ -81,9 +81,12 @@ bool ConfigFilesHandler::HandleRequest( response.result(http::status::ok); response.set(http::field::content_type, "application/octet-stream"); response.SendFile(path, yc); - } catch (const std::exception& ex) { - HttpUtility::SendJsonError(response, params, 500, "Could not read file.", - DiagnosticInformation(ex)); + } catch (const std::ios_base::failure& ex) { + if (response.HasSerializationStarted()) { + throw; + } + + HttpUtility::SendJsonError(response, params, 500, "Could not read file.", DiagnosticInformation(ex)); } return true; From fe469a5455c59fc0dd9098fe45f78de75b02486a Mon Sep 17 00:00:00 2001 From: Johannes Schmidt Date: Tue, 2 Jun 2026 10:19:02 +0200 Subject: [PATCH 4/4] Guard against assert() failures in SendJsonError() Co-authored-by: Julian Brost --- lib/remote/httputility.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/remote/httputility.cpp b/lib/remote/httputility.cpp index 5e466bb8a..f868dae81 100644 --- a/lib/remote/httputility.cpp +++ b/lib/remote/httputility.cpp @@ -86,6 +86,16 @@ void HttpUtility::SendJsonBody(HttpApiResponse& response, const Dictionary::Ptr& void HttpUtility::SendJsonError(HttpApiResponse& response, const Dictionary::Ptr& params, int code, const String& info, const String& diagnosticInformation) { + if (response.HasSerializationStarted()) { + std::ostringstream err; + err << "Impossible to send error response after streaming has started: error: '" << code << "', status: '" + << info << "'"; + if (!diagnosticInformation.IsEmpty()) { + err << ", diagnostic_information: '" << diagnosticInformation << "'"; + } + BOOST_THROW_EXCEPTION(std::logic_error{err.str()}); + } + Dictionary::Ptr result = new Dictionary({ { "error", code } }); if (!info.IsEmpty()) {