// GNU Lesser General Public License v3.0 // Copyright (c) 2025 Bart Beumer // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License v3.0 as published by // the Free Software Foundation. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mandelbrot.hpp" namespace { const std::regex regex_renderpath(R"REGEX(\/render_z(\d+)x(-?\d+)y(-?\d+)w(\d+)h(\d+)ar(-?\d+\.\d+)ai(-?\d+\.\d+)maxiter(\d+).jpg)REGEX"); constexpr unsigned int config_max_simultanious_requests = 10; constexpr uint64_t config_request_body_limit = (10 * 1024); constexpr std::chrono::seconds config_request_timeout(3); const boost::asio::ip::tcp::endpoint listen_endpoint(boost::asio::ip::tcp::v4(), 9800); class split_req : public bmrshared::web::request_handler_interface { public: split_req(const std::filesystem::path& root_dir, boost::asio::io_context& ioc) : m_dir(root_dir), m_ioc(ioc) {} ~split_req() override = default; void handle_request_http(bmrshared::web::request_response rs) override { auto& req = rs.get_request(); std::string target = req.target(); if (req.method() != boost::beast::http::verb::get) { // Other methods are not supported for directory acces // auto& bad_request = rs.create_response>(boost::beast::http::status::bad_request, req.version()); bad_request.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); bad_request.set(boost::beast::http::field::content_type, "text/plain"); bad_request.prepare_payload(); bad_request.body() = "Bad request type.\nOnly GET and HEAD are expected for this URL."; bad_request.keep_alive(req.keep_alive()); return; } std::cout << "GET: " << target << std::endl; std::smatch base_match; std::string tmp(target); if (std::regex_match(tmp, base_match, regex_renderpath) && base_match.size() == 9) { int z = std::stoi(base_match[1]); int offset_y = std::stoi(base_match[2]); int offset_x = std::stoi(base_match[3]); int width = std::stoi(base_match[4]); int height = std::stoi(base_match[5]); double a_real = std::stof(base_match[6]); double a_img = std::stof(base_match[7]); int max_iter = std::stoi(base_match[8]); std::complex factor_a(a_real, a_img); auto renderfn = [offset_y, offset_x, z, width, height, factor_a, max_iter, req, target, rs, tmp]() mutable { int pixel_width = width; int pixel_height = height; int max_iterations = max_iter; boost::gil::rgb8_image_t image(pixel_width,pixel_height); auto view = boost::gil::view(image); boost::gil::fill_pixels(view, boost::gil::rgb8_pixel_t(0)); auto painter = [&](int x, int y, int num_iter) -> void { if(x >= 0 && y >= 0 && x < pixel_width && y < pixel_height) { bmrshared::color c({.h=(num_iter*2)%360,.s=1.0, .v=1.0}); auto crgb = c.rgb(); uint8_t r = crgb.r * 255; uint8_t g = crgb.g * 255; uint8_t b = crgb.b * 255; const auto iter = view.at({x, y}); boost::gil::color_convert(boost::gil::rgb8_pixel_t(r,g,b), *iter); } }; const auto zoomfactor = 1.0/std::pow(2, z); window src_window{offset_x * zoomfactor, (offset_x + 1)*zoomfactor, offset_y * zoomfactor ,(offset_y + 1)*zoomfactor}; window dst_window{0,pixel_width, 0,pixel_height}; mandelbrot fractal(src_window, dst_window, max_iterations, factor_a); fractal.apply(painter); std::stringstream out_buffer( std::ios_base::out | std::ios_base::binary ); boost::gil::write_view(out_buffer, boost::gil::view(image), boost::gil::image_write_info(90)); auto& ok = rs.create_response>(boost::beast::http::status::ok, req.version()); ok.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); ok.set(boost::beast::http::field::content_type, "image/jpeg"); ok.body() = out_buffer.str(); ok.keep_alive(req.keep_alive()); ok.prepare_payload(); }; boost::asio::post(m_ioc, renderfn); } else { m_dir.handle_request_http(rs); } } void handle_request_websocket_upgrade(const address_type& addr, const request_type& req, boost::asio::ip::tcp::socket socket) override { m_dir.handle_request_websocket_upgrade(addr, req, std::move(socket)); ; } private: bmrshared::web::directory_request_handler m_dir; boost::asio::io_context& m_ioc; }; } int main(int argc, char **argv) { std::filesystem::path root_dir = "/workspaces/network-experiment/applications/http-mandelbrot/html"; boost::asio::io_context ioc; split_req request_handler(root_dir, ioc); boost::asio::signal_set signals(ioc, SIGINT, SIGTERM); std::shared_ptr httpserver; signals.async_wait([&ioc](const boost::system::error_code& error, int signal_number) { ioc.stop(); }); boost::asio::post(ioc, [&ioc, &httpserver, &request_handler]{ httpserver = std::make_shared( ioc, listen_endpoint, config_request_timeout, config_request_body_limit, config_max_simultanious_requests, request_handler); }); std::vector threads; while(threads.size() < 4) { threads.emplace_back([&ioc]{ioc.run();}); } ioc.run(); threads.clear(); }