diff --git a/broker/JFJochBrokerHttp.cpp b/broker/JFJochBrokerHttp.cpp index 214dad90..645e7766 100644 --- a/broker/JFJochBrokerHttp.cpp +++ b/broker/JFJochBrokerHttp.cpp @@ -17,7 +17,49 @@ using namespace org::openapitools::server::model; namespace { - inline std::string mime_from_path(const std::string &path) { + template + bool fromStringValue(const std::string &s, T &out) { + std::istringstream is(s); + is >> out; + return !is.fail() && is.eof(); + } + + template<> + bool fromStringValue(const std::string &s, std::string &out) { + out = s; + return true; + } + + template<> + bool fromStringValue(const std::string &s, bool &out) { + if (s == "true" || s == "1") { + out = true; + return true; + } + if (s == "false" || s == "0") { + out = false; + return true; + } + return false; + } + + template + std::optional parse_query_value(const httplib::Request &req, const char *name) { + if (!req.has_param(name)) + return std::nullopt; + T v{}; + if (fromStringValue(req.get_param_value(name), v)) + return v; + return std::nullopt; + } + + std::optional parse_query_string(const httplib::Request &req, const char *name) { + if (!req.has_param(name)) + return std::nullopt; + return req.get_param_value(name); + } + + std::string mime_from_path(const std::string &path) { if (path.ends_with(".js")) return "text/javascript"; if (path.ends_with(".css")) return "text/css"; if (path.ends_with(".html")) return "text/html"; @@ -44,7 +86,8 @@ namespace { JFJochBrokerHttp::JFJochBrokerHttp(const DiffractionExperiment &experiment, const SpotFindingSettings &spot_finding_settings) - : state_machine(experiment, services, logger, spot_finding_settings) {} + : state_machine(experiment, services, logger, spot_finding_settings) { +} void JFJochBrokerHttp::AddDetectorSetup(const DetectorSetup &setup) { state_machine.AddDetectorSetup(setup); @@ -154,13 +197,13 @@ void JFJochBrokerHttp::register_routes(httplib::Server &server) { server.Get("/config/instrument", bind_noarg(&JFJochBrokerHttp::config_instrument_get)); server.Put("/config/instrument", bind_json(&JFJochBrokerHttp::config_instrument_put, Instrument_metadata{})); server.Put("/config/internal_generator_image", [this](const httplib::Request &req, httplib::Response &res) { - try { - config_internal_generator_image_put(parse_query_value(req, "id"), req, res); - } catch (const std::exception &e) { - auto [c, s] = handleOperationException(e); - send_plain(res, c, s); - } - }); + try { + config_internal_generator_image_put(parse_query_value(req, "id"), req, res); + } catch (const std::exception &e) { + auto [c, s] = handleOperationException(e); + send_plain(res, c, s); + } + }); server.Put("/config/internal_generator_image.tiff", [this](const httplib::Request &req, httplib::Response &res) { try { config_internal_generator_image_tiff_put(parse_query_value(req, "id"), req, res); @@ -168,12 +211,14 @@ void JFJochBrokerHttp::register_routes(httplib::Server &server) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } - }); server.Get("/config/mask", bind_noarg(&JFJochBrokerHttp::config_mask_get)); + }); + server.Get("/config/mask", bind_noarg(&JFJochBrokerHttp::config_mask_get)); server.Get("/config/mask.tiff", bind_noarg(&JFJochBrokerHttp::config_mask_tiff_get)); server.Get("/config/roi", bind_noarg(&JFJochBrokerHttp::config_roi_get)); server.Put("/config/roi", bind_json(&JFJochBrokerHttp::config_roi_put, Roi_definitions{})); server.Get("/config/select_detector", bind_noarg(&JFJochBrokerHttp::config_select_detector_get)); - server.Put("/config/select_detector", bind_json(&JFJochBrokerHttp::config_select_detector_put, Detector_selection{})); + server.Put("/config/select_detector", + bind_json(&JFJochBrokerHttp::config_select_detector_put, Detector_selection{})); server.Get("/config/spot_finding", bind_noarg(&JFJochBrokerHttp::config_spot_finding_get)); server.Put("/config/spot_finding", bind_json(&JFJochBrokerHttp::config_spot_finding_put, Spot_finding_settings{})); server.Get("/config/user_mask", bind_noarg(&JFJochBrokerHttp::config_user_mask_get)); @@ -181,9 +226,11 @@ void JFJochBrokerHttp::register_routes(httplib::Server &server) { server.Get("/config/user_mask.tiff", bind_noarg(&JFJochBrokerHttp::config_user_mask_tiff_get)); server.Put("/config/user_mask.tiff", bind_req(&JFJochBrokerHttp::config_user_mask_tiff_put)); server.Get("/config/zeromq_metadata", bind_noarg(&JFJochBrokerHttp::config_zeromq_metadata_get)); - server.Put("/config/zeromq_metadata", bind_json(&JFJochBrokerHttp::config_zeromq_metadata_put, Zeromq_metadata_settings{})); + server.Put("/config/zeromq_metadata", + bind_json(&JFJochBrokerHttp::config_zeromq_metadata_put, Zeromq_metadata_settings{})); server.Get("/config/zeromq_preview", bind_noarg(&JFJochBrokerHttp::config_zeromq_preview_get)); - server.Put("/config/zeromq_preview", bind_json(&JFJochBrokerHttp::config_zeromq_preview_put, Zeromq_preview_settings{})); + server.Put("/config/zeromq_preview", + bind_json(&JFJochBrokerHttp::config_zeromq_preview_put, Zeromq_preview_settings{})); server.Post("/deactivate", bind_noarg(&JFJochBrokerHttp::deactivate_post)); server.Get("/detector/status", bind_noarg(&JFJochBrokerHttp::detector_status_get)); server.Get("/fpga_status", bind_noarg(&JFJochBrokerHttp::fpga_status_get)); @@ -237,8 +284,8 @@ void JFJochBrokerHttp::register_routes(httplib::Server &server) { server.Get("/preview/pedestal.tiff", [this](const httplib::Request &req, httplib::Response &res) { try { preview_pedestal_tiff_get(parse_query_value(req, "gain_level"), - parse_query_value(req, "sc"), - res); + parse_query_value(req, "sc"), + res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); @@ -300,11 +347,6 @@ void JFJochBrokerHttp::register_routes(httplib::Server &server) { server.Get("/xfel/event_code", bind_noarg(&JFJochBrokerHttp::xfel_event_code_get)); server.Get("/xfel/pulse_id", bind_noarg(&JFJochBrokerHttp::xfel_pulse_id_get)); - - server.Get("/", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); }); - server.Get("/frontend", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); }); - server.Get("/frontend/*", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); }); - server.Get("/frontend/assets/*", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); }); } void JFJochBrokerHttp::cancel_post(httplib::Response &response) { @@ -415,7 +457,7 @@ void JFJochBrokerHttp::config_spot_finding_put(const Spot_finding_settings &spot void JFJochBrokerHttp::statistics_calibration_get(httplib::Response &response) { nlohmann::json j; - for (const auto &d : Convert(state_machine.GetCalibrationStatistics())) + for (const auto &d: Convert(state_machine.GetCalibrationStatistics())) j.push_back(d); response.set_content(j.dump(), "application/json"); response.status = 200; @@ -461,7 +503,7 @@ void JFJochBrokerHttp::config_image_format_raw_post(httplib::Response &response) void JFJochBrokerHttp::fpga_status_get(httplib::Response &response) { nlohmann::json j; - for (const auto &d : Convert(state_machine.GetDeviceStatus())) + for (const auto &d: Convert(state_machine.GetDeviceStatus())) j.push_back(d); response.set_content(j.dump(), "application/json"); response.status = 200; @@ -788,12 +830,12 @@ void JFJochBrokerHttp::preview_plot_bin_get(const std::optional &ty } void JFJochBrokerHttp::preview_plot_get(const std::optional &type, - const std::optional &binning, - const std::optional &compression, - const std::optional &fill, - const std::optional &experimentalCoord, - const std::optional &azintUnit, - httplib::Response &response) { + const std::optional &binning, + const std::optional &compression, + const std::optional &fill, + const std::optional &experimentalCoord, + const std::optional &azintUnit, + httplib::Response &response) { PlotAzintUnit unit = PlotAzintUnit::Q_recipA; if (azintUnit.has_value()) { if (azintUnit == "Q_recipA" || azintUnit == "q_recipa") @@ -854,44 +896,3 @@ void JFJochBrokerHttp::xfel_pulse_id_get(httplib::Response &response) { response.set_content(j.dump(), "application/json"); response.status = 200; } - -void JFJochBrokerHttp::GetStaticFile(const httplib::Request &request, httplib::Response &response) { - if (frontend_directory.empty()) { - response.status = 404; - return; - } - - const std::string path = request.path; - logger.Info("Requesting static resource {} from {}", path, frontend_directory); - - if (path.find("../") != std::string::npos) { - response.status = 403; - return; - } - - std::string rel; - if (path == "/" || path == "/frontend" || path == "/frontend/") { - rel = "index.html"; - } else if (path.starts_with("/frontend/")) { - rel = path.substr(std::string("/frontend/").size()); - } else { - response.status = 404; - return; - } - - const std::string full = frontend_directory + "/" + rel; - - try { - std::string body; - if (!read_file_to_string(full, body)) { - response.status = 404; - return; - } - - response.set_content(std::move(body), mime_from_path(full)); - response.status = 200; - } catch (const std::exception &e) { - logger.Error(e.what()); - response.status = 404; - } -} diff --git a/broker/JFJochBrokerHttp.h b/broker/JFJochBrokerHttp.h index 864a09d8..64072297 100644 --- a/broker/JFJochBrokerHttp.h +++ b/broker/JFJochBrokerHttp.h @@ -44,48 +44,6 @@ class JFJochBrokerHttp { JFJochStateMachine state_machine; std::string frontend_directory; - template - static bool fromStringValue(const std::string &s, T &out) { - std::istringstream is(s); - is >> out; - return !is.fail() && is.eof(); - } - - template<> - static bool fromStringValue(const std::string &s, std::string &out) { - out = s; - return true; - } - - template<> - static bool fromStringValue(const std::string &s, bool &out) { - if (s == "true" || s == "1") { - out = true; - return true; - } - if (s == "false" || s == "0") { - out = false; - return true; - } - return false; - } - - template - static std::optional parse_query_value(const httplib::Request &req, const char *name) { - if (!req.has_param(name)) - return std::nullopt; - T v{}; - if (fromStringValue(req.get_param_value(name), v)) - return v; - return std::nullopt; - } - - static std::optional parse_query_string(const httplib::Request &req, const char *name) { - if (!req.has_param(name)) - return std::nullopt; - return req.get_param_value(name); - } - static void send_plain(httplib::Response &res, int code, const std::string &body) { res.status = code; res.set_content(body, "text/plain"); @@ -226,8 +184,6 @@ class JFJochBrokerHttp { void xfel_event_code_get(httplib::Response &response); void xfel_pulse_id_get(httplib::Response &response); - void GetStaticFile(const httplib::Request &request, httplib::Response &response); - public: JFJochBrokerHttp(const DiffractionExperiment &experiment, const SpotFindingSettings &spot_finding_settings); diff --git a/broker/jfjoch_broker.cpp b/broker/jfjoch_broker.cpp index 79e024d9..dde40cbe 100644 --- a/broker/jfjoch_broker.cpp +++ b/broker/jfjoch_broker.cpp @@ -118,6 +118,12 @@ int main(int argc, char **argv) { httplib::Server server; httpServer = &server; + server.Get("/", [](const httplib::Request &req, httplib::Response &res) { + res.status = 302; // Found (temporary redirect) + res.set_header("Location", "/frontend"); + }); + server.set_mount_point("/frontend", settings.getFrontendDirectory()); + signal(SIGPIPE, SIG_IGN); std::vector sigs{SIGQUIT, SIGINT, SIGTERM, SIGHUP}; setUpUnixSignals(sigs);