Compare commits
No commits in common. "63be32de7cedabd15fd10dfb5419283c5d0884ea" and "06cbd98b77615f66c8a568ed77ca25518d76cc0c" have entirely different histories.
63be32de7c
...
06cbd98b77
|
|
@ -31,12 +31,12 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="map" style="width: 100%; height: 100%;"></div>
|
<div id="map" style="width: 1500px; height: 1500px;"></div>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const map = L.map('map', {crs: L.CRS.Simple}).setView([0.0, 0.0], 1);
|
const map = L.map('map', {crs: L.CRS.Simple}).setView([0.0, 0.0], 1);
|
||||||
|
|
||||||
const tiles = L.tileLayer('http://localhost:9800/render_{x}_{y}_{z}', {
|
const tiles = L.tileLayer('/render_{x}_{y}_{z}', {
|
||||||
maxZoom: 1000
|
maxZoom: 1000
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@
|
||||||
#include <bmrshared/server.hpp>
|
#include <bmrshared/server.hpp>
|
||||||
#include <bmrshared/request_handler_interface.hpp>
|
#include <bmrshared/request_handler_interface.hpp>
|
||||||
#include <bmrshared/directory_request_handler.hpp>
|
#include <bmrshared/directory_request_handler.hpp>
|
||||||
#include <bmrshared/request_response.hpp>
|
#include <bmrshared/response_promise.hpp>
|
||||||
|
|
||||||
#include <boost/asio/signal_set.hpp>
|
#include <boost/asio/signal_set.hpp>
|
||||||
#include <boost/gil/image.hpp>
|
#include <boost/gil/image.hpp>
|
||||||
#include <boost/gil/image_view.hpp>
|
#include <boost/gil/image_view.hpp>
|
||||||
|
|
@ -23,7 +24,7 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const std::regex regex_renderpath(R"REGEX(\/render_(-?\d+)_(-?\d+)_(\d+))REGEX");
|
const std::regex regex_renderpath(R"REGEX(\/render_(\d+)_(\d+)_(\d+))REGEX");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -105,9 +106,9 @@ void fractal(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
constexpr unsigned int config_max_simultanious_requests = 10;
|
constexpr unsigned int config_max_simultanious_requests = 50;
|
||||||
constexpr uint64_t config_request_body_limit = (10 * 1024);
|
constexpr uint64_t config_request_body_limit = (10 * 1024);
|
||||||
constexpr std::chrono::seconds config_request_timeout(3);
|
constexpr std::chrono::seconds config_request_timeout(30);
|
||||||
|
|
||||||
const boost::asio::ip::tcp::endpoint listen_endpoint(boost::asio::ip::tcp::v4(), 9800);
|
const boost::asio::ip::tcp::endpoint listen_endpoint(boost::asio::ip::tcp::v4(), 9800);
|
||||||
|
|
||||||
|
|
@ -123,15 +124,15 @@ void fractal(
|
||||||
|
|
||||||
~split_req() override = default;
|
~split_req() override = default;
|
||||||
|
|
||||||
void handle_request_http(bmrshared::web::request_response rs) override
|
void handle_request_http(const address_type& addr, const request_type& req, bmrshared::web::response_promise promise) override
|
||||||
{
|
{
|
||||||
auto& req = rs.get_request();
|
|
||||||
std::string target = req.target();
|
std::string target = req.target();
|
||||||
|
std::cout << "HTTP GET: " << target << std::endl;
|
||||||
if (req.method() != boost::beast::http::verb::get)
|
if (req.method() != boost::beast::http::verb::get)
|
||||||
{
|
{
|
||||||
// Other methods are not supported for directory acces
|
// Other methods are not supported for directory acces
|
||||||
//
|
//
|
||||||
auto& bad_request = rs.create_response<boost::beast::http::response<boost::beast::http::string_body>>(boost::beast::http::status::bad_request, req.version());
|
auto& bad_request = promise.CreateResponse<boost::beast::http::response<boost::beast::http::string_body>>(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::server, BOOST_BEAST_VERSION_STRING);
|
||||||
bad_request.set(boost::beast::http::field::content_type, "text/plain");
|
bad_request.set(boost::beast::http::field::content_type, "text/plain");
|
||||||
bad_request.prepare_payload();
|
bad_request.prepare_payload();
|
||||||
|
|
@ -148,8 +149,9 @@ void fractal(
|
||||||
double offset_x = std::stoi(base_match[2]);
|
double offset_x = std::stoi(base_match[2]);
|
||||||
double z = std::stoi(base_match[3]);
|
double z = std::stoi(base_match[3]);
|
||||||
|
|
||||||
auto renderfn = [offset_y, offset_x, z, req, target, rs, tmp]() mutable
|
auto renderfn = [offset_y, offset_x, z, req, target, promise]() mutable
|
||||||
{
|
{
|
||||||
|
std::cout << "HTTP inside render function " << target << std::endl;
|
||||||
constexpr int pixel_width = 256;
|
constexpr int pixel_width = 256;
|
||||||
constexpr int pixel_height = 256;
|
constexpr int pixel_height = 256;
|
||||||
constexpr int max_iterations = 255;
|
constexpr int max_iterations = 255;
|
||||||
|
|
@ -164,7 +166,7 @@ void fractal(
|
||||||
if(x >= 0 && y >= 0 && x < pixel_width && y < pixel_height)
|
if(x >= 0 && y >= 0 && x < pixel_width && y < pixel_height)
|
||||||
{
|
{
|
||||||
const auto iter = view.at({x, y});
|
const auto iter = view.at({x, y});
|
||||||
boost::gil::color_convert(boost::gil::gray8_pixel_t(num_iter), *iter);
|
boost::gil::color_convert(boost::gil::gray8_pixel_t(num_iter%256), *iter);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -184,21 +186,24 @@ void fractal(
|
||||||
std::stringstream out_buffer( std::ios_base::out | std::ios_base::binary );
|
std::stringstream out_buffer( std::ios_base::out | std::ios_base::binary );
|
||||||
boost::gil::write_view(out_buffer,
|
boost::gil::write_view(out_buffer,
|
||||||
boost::gil::view(image),
|
boost::gil::view(image),
|
||||||
boost::gil::image_write_info<boost::gil::jpeg_tag>(70));
|
boost::gil::image_write_info<boost::gil::jpeg_tag>(95));
|
||||||
|
|
||||||
auto& ok = rs.create_response<boost::beast::http::response<boost::beast::http::string_body>>(boost::beast::http::status::ok, req.version());
|
auto& ok = promise.CreateResponse<boost::beast::http::response<boost::beast::http::string_body>>(boost::beast::http::status::ok, req.version());
|
||||||
ok.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING);
|
ok.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
ok.set(boost::beast::http::field::content_type, "image/jpeg");
|
ok.set(boost::beast::http::field::content_type, "image/jpeg");
|
||||||
ok.body() = out_buffer.str();
|
ok.body() = out_buffer.str();
|
||||||
ok.keep_alive(req.keep_alive());
|
ok.keep_alive(req.keep_alive());
|
||||||
ok.prepare_payload();
|
ok.prepare_payload();
|
||||||
|
|
||||||
|
std::cout << " DONE HTTP inside render function " << target << std::endl;
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::asio::post(m_ioc, renderfn);
|
m_ioc.post(renderfn);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_dir.handle_request_http(rs);
|
m_dir.handle_request_http(addr, req, promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,7 +237,7 @@ int main(int argc, char **argv)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
boost::asio::post(ioc, [&ioc, &httpserver, &request_handler]{
|
ioc.post([&ioc, &httpserver, &request_handler]{
|
||||||
httpserver = std::make_shared<bmrshared::web::server>(
|
httpserver = std::make_shared<bmrshared::web::server>(
|
||||||
ioc,
|
ioc,
|
||||||
listen_endpoint,
|
listen_endpoint,
|
||||||
|
|
@ -242,10 +247,8 @@ int main(int argc, char **argv)
|
||||||
request_handler);
|
request_handler);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::jthread> threads;
|
std::vector<std::jthread> threads;
|
||||||
while(threads.size() < 16)
|
while(threads.size() < 4)
|
||||||
{
|
{
|
||||||
threads.emplace_back([&ioc]{ioc.run();});
|
threads.emplace_back([&ioc]{ioc.run();});
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +256,4 @@ int main(int argc, char **argv)
|
||||||
ioc.run();
|
ioc.run();
|
||||||
|
|
||||||
threads.clear();
|
threads.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class directory_request_handler : public request_handler_interface
|
||||||
explicit directory_request_handler(const std::filesystem::path& document_root);
|
explicit directory_request_handler(const std::filesystem::path& document_root);
|
||||||
~directory_request_handler() override;
|
~directory_request_handler() override;
|
||||||
|
|
||||||
void handle_request_http(request_response rs) override;
|
void handle_request_http(const address_type& address, const request_type& req, response_promise promise) override;
|
||||||
|
|
||||||
void handle_request_websocket_upgrade(const address_type& address,
|
void handle_request_websocket_upgrade(const address_type& address,
|
||||||
const request_type& req,
|
const request_type& req,
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "server.hpp"
|
#include "server.hpp"
|
||||||
#include <bmrshared/request_response.hpp>
|
|
||||||
|
|
||||||
namespace bmrshared::web
|
namespace bmrshared::web
|
||||||
{
|
{
|
||||||
|
class response_promise;
|
||||||
|
|
||||||
class request_handler_interface
|
class request_handler_interface
|
||||||
{
|
{
|
||||||
|
|
@ -20,7 +20,7 @@ class request_handler_interface
|
||||||
|
|
||||||
virtual ~request_handler_interface() = default;
|
virtual ~request_handler_interface() = default;
|
||||||
|
|
||||||
virtual void handle_request_http(request_response rs) = 0;
|
virtual void handle_request_http(const address_type&, const request_type&, response_promise) = 0;
|
||||||
virtual void handle_request_websocket_upgrade(const address_type&, const request_type&, boost::asio::ip::tcp::socket) = 0;
|
virtual void handle_request_websocket_upgrade(const address_type&, const request_type&, boost::asio::ip::tcp::socket) = 0;
|
||||||
};
|
};
|
||||||
} // namespace bmrshared::web
|
} // namespace bmrshared::web
|
||||||
|
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
// GNU Lesser General Public License v3.0
|
|
||||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "server.hpp"
|
|
||||||
#include <boost/beast.hpp>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace bmrshared::web
|
|
||||||
{
|
|
||||||
class request_response final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using response_sender = std::function<void(boost::beast::tcp_stream& stream)>;
|
|
||||||
using request_type = bmrshared::web::server::request_type;
|
|
||||||
using callback_on_finalize = std::function<void(response_sender send_response)>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class response_writer_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~response_writer_interface();
|
|
||||||
virtual void write_response(boost::beast::tcp_stream&) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename response_type>
|
|
||||||
class response_writer final : public response_writer_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template<typename... TArgs>
|
|
||||||
response_writer(TArgs&&... args)
|
|
||||||
: m_response(std::forward<TArgs>(args)...)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~response_writer() override = default;
|
|
||||||
|
|
||||||
void write_response(boost::beast::tcp_stream& stream) override
|
|
||||||
{
|
|
||||||
boost::beast::http::write(stream, m_response);
|
|
||||||
}
|
|
||||||
|
|
||||||
response_type& response()
|
|
||||||
{
|
|
||||||
return m_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
response_type m_response;
|
|
||||||
};
|
|
||||||
|
|
||||||
class internal_request_response final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Disabling copying and moving.
|
|
||||||
internal_request_response() = delete;
|
|
||||||
internal_request_response(const internal_request_response&) = delete;
|
|
||||||
internal_request_response(internal_request_response&&) = delete;
|
|
||||||
internal_request_response& operator=(const internal_request_response&) = delete;
|
|
||||||
internal_request_response& operator=(internal_request_response&&) = delete;
|
|
||||||
|
|
||||||
|
|
||||||
internal_request_response(request_type request, callback_on_finalize cb_finalize);
|
|
||||||
~internal_request_response();
|
|
||||||
|
|
||||||
const request_type& get_request() const;
|
|
||||||
void set_response_sender(std::unique_ptr<response_writer_interface> response_writer);
|
|
||||||
|
|
||||||
private:
|
|
||||||
request_type m_request;
|
|
||||||
callback_on_finalize m_cb_finalize;
|
|
||||||
std::unique_ptr<response_writer_interface> m_response_writer;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
request_response() = delete;
|
|
||||||
request_response(const request_response&) = default;
|
|
||||||
request_response(request_response&&) = default;
|
|
||||||
request_response& operator=(const request_response&) = default;
|
|
||||||
request_response& operator=(request_response&&) = default;
|
|
||||||
|
|
||||||
request_response(request_type request, callback_on_finalize cb_finalize);
|
|
||||||
~request_response();
|
|
||||||
|
|
||||||
template<typename response_type, typename... arg_types>
|
|
||||||
response_type& create_response(arg_types&&... args)
|
|
||||||
{
|
|
||||||
auto created = std::make_unique<response_writer<response_type>>(std::forward<arg_types>(args)...);
|
|
||||||
auto& resp = created->response();
|
|
||||||
m_internal->set_response_sender(std::move(created));
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const request_type& get_request() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<internal_request_response> m_internal;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// GNU Lesser General Public License v3.0
|
||||||
|
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
#include <boost/asio/io_context.hpp>
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/beast.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace bmrshared::web
|
||||||
|
{
|
||||||
|
|
||||||
|
class response_promise final
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
class response_writer_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~response_writer_interface() = default;
|
||||||
|
virtual void write_response(boost::beast::tcp_stream&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TResponse>
|
||||||
|
class response_writer : public response_writer_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<typename... TArgs>
|
||||||
|
response_writer(TArgs... args)
|
||||||
|
: m_response(args...)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~response_writer() override = default;
|
||||||
|
|
||||||
|
void write_response(boost::beast::tcp_stream& stream) override
|
||||||
|
{
|
||||||
|
boost::beast::http::write(stream, m_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
TResponse& response()
|
||||||
|
{
|
||||||
|
return m_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TResponse m_response;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
using response_sender = std::function<void(boost::beast::tcp_stream& stream)>;
|
||||||
|
using callback_on_response = std::function<void(response_sender fnSendResponse)>;
|
||||||
|
|
||||||
|
response_promise() = delete;
|
||||||
|
explicit response_promise(callback_on_response cbOnResponse);
|
||||||
|
~response_promise();
|
||||||
|
|
||||||
|
template<typename TResponse, typename... TArgs>
|
||||||
|
TResponse& CreateResponse(TArgs&&... args)
|
||||||
|
{
|
||||||
|
auto created = std::make_unique<response_writer<TResponse>>(std::forward<TArgs>(args)...);
|
||||||
|
auto& resp = created->response();
|
||||||
|
m_response_writer = std::move(created);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
callback_on_response m_call_on_response;
|
||||||
|
std::shared_ptr<response_writer_interface> m_response_writer;
|
||||||
|
};
|
||||||
|
} // namespace bmrshared::web
|
||||||
|
|
@ -7,7 +7,7 @@ add_library(
|
||||||
${PROJECT_NAME}
|
${PROJECT_NAME}
|
||||||
directory_request_handler.cpp
|
directory_request_handler.cpp
|
||||||
internal_server.cpp
|
internal_server.cpp
|
||||||
request_response.cpp
|
response_promise.cpp
|
||||||
server.cpp
|
server.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <bmrshared/magic_file_info.hpp>
|
#include <bmrshared/magic_file_info.hpp>
|
||||||
|
#include <bmrshared/response_promise.hpp>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
@ -41,14 +42,15 @@ directory_request_handler::directory_request_handler(const std::filesystem::path
|
||||||
|
|
||||||
directory_request_handler::~directory_request_handler() = default;
|
directory_request_handler::~directory_request_handler() = default;
|
||||||
|
|
||||||
void directory_request_handler::handle_request_http(request_response rs)
|
void directory_request_handler::handle_request_http(const address_type& address,
|
||||||
|
const request_type& req,
|
||||||
|
bmrshared::web::response_promise promise)
|
||||||
{
|
{
|
||||||
const auto& req = rs.get_request();
|
|
||||||
if ((req.method() != http::verb::get) && (req.method() != http::verb::head))
|
if ((req.method() != http::verb::get) && (req.method() != http::verb::head))
|
||||||
{
|
{
|
||||||
// Other methods are not supported for directory acces
|
// Other methods are not supported for directory acces
|
||||||
//
|
//
|
||||||
auto& bad_request = rs.create_response<http::response<http::string_body>>(http::status::bad_request, req.version());
|
auto& bad_request = promise.CreateResponse<http::response<http::string_body>>(http::status::bad_request, req.version());
|
||||||
bad_request.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
bad_request.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
bad_request.set(http::field::content_type, "text/plain");
|
bad_request.set(http::field::content_type, "text/plain");
|
||||||
bad_request.prepare_payload();
|
bad_request.prepare_payload();
|
||||||
|
|
@ -83,7 +85,7 @@ void directory_request_handler::handle_request_http(request_response rs)
|
||||||
|
|
||||||
if (!found_file)
|
if (!found_file)
|
||||||
{
|
{
|
||||||
auto& not_found = rs.create_response<http::response<http::string_body>>(http::status::not_found, req.version());
|
auto& not_found = promise.CreateResponse<http::response<http::string_body>>(http::status::not_found, req.version());
|
||||||
not_found.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
not_found.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
not_found.set(http::field::content_type, "text/plain");
|
not_found.set(http::field::content_type, "text/plain");
|
||||||
not_found.prepare_payload();
|
not_found.prepare_payload();
|
||||||
|
|
@ -98,7 +100,7 @@ void directory_request_handler::handle_request_http(request_response rs)
|
||||||
body.open(found_file->native().c_str(), beast::file_mode::scan, ec);
|
body.open(found_file->native().c_str(), beast::file_mode::scan, ec);
|
||||||
if (ec)
|
if (ec)
|
||||||
{
|
{
|
||||||
auto& internal = rs.create_response<http::response<http::string_body>>(http::status::internal_server_error, req.version());
|
auto& internal = promise.CreateResponse<http::response<http::string_body>>(http::status::internal_server_error, req.version());
|
||||||
internal.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
internal.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
internal.set(http::field::content_type, "text/plain");
|
internal.set(http::field::content_type, "text/plain");
|
||||||
internal.prepare_payload();
|
internal.prepare_payload();
|
||||||
|
|
@ -114,7 +116,7 @@ void directory_request_handler::handle_request_http(request_response rs)
|
||||||
|
|
||||||
if (req.count(http::field::if_modified_since) != 0 && (req[http::field::if_modified_since] == last_modified))
|
if (req.count(http::field::if_modified_since) != 0 && (req[http::field::if_modified_since] == last_modified))
|
||||||
{
|
{
|
||||||
auto& head = rs.create_response<http::response<http::string_body>>(http::status::not_modified, req.version());
|
auto& head = promise.CreateResponse<http::response<http::string_body>>(http::status::not_modified, req.version());
|
||||||
head.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
head.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
head.keep_alive(req.keep_alive());
|
head.keep_alive(req.keep_alive());
|
||||||
return;
|
return;
|
||||||
|
|
@ -124,7 +126,7 @@ void directory_request_handler::handle_request_http(request_response rs)
|
||||||
// Okay, we have a file so let us send the correct response.
|
// Okay, we have a file so let us send the correct response.
|
||||||
if (req.method() == http::verb::head)
|
if (req.method() == http::verb::head)
|
||||||
{
|
{
|
||||||
auto& head = rs.create_response<http::response<http::string_body>>(http::status::ok, req.version());
|
auto& head = promise.CreateResponse<http::response<http::string_body>>(http::status::ok, req.version());
|
||||||
head.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
head.set(http::field::server, BOOST_BEAST_VERSION_STRING);
|
||||||
head.set(http::field::content_type, mime_type);
|
head.set(http::field::content_type, mime_type);
|
||||||
head.set(http::field::last_modified, last_modified);
|
head.set(http::field::last_modified, last_modified);
|
||||||
|
|
@ -134,8 +136,8 @@ void directory_request_handler::handle_request_http(request_response rs)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (req.method() == http::verb::get)
|
else if (req.method() == http::verb::get)
|
||||||
{
|
{/*
|
||||||
http::response<http::file_body> head(std::piecewise_construct,
|
auto& head = promise.CreateResponse<http::response<http::file_body>>(std::piecewise_construct,
|
||||||
std::make_tuple(std::move(body)),
|
std::make_tuple(std::move(body)),
|
||||||
std::make_tuple(http::status::ok, req.version()));
|
std::make_tuple(http::status::ok, req.version()));
|
||||||
|
|
||||||
|
|
@ -144,10 +146,8 @@ void directory_request_handler::handle_request_http(request_response rs)
|
||||||
head.set(http::field::last_modified, last_modified);
|
head.set(http::field::last_modified, last_modified);
|
||||||
head.set(http::field::cache_control, "public, max-age=360");
|
head.set(http::field::cache_control, "public, max-age=360");
|
||||||
head.set(http::field::content_length, std::to_string(file_size));
|
head.set(http::field::content_length, std::to_string(file_size));
|
||||||
head.keep_alive(req.keep_alive());
|
head.keep_alive(req.keep_alive());*/
|
||||||
rs.create_response<http::response<http::file_body>>(std::move(head));
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#include "internal_server.h"
|
#include "internal_server.h"
|
||||||
#include <bmrshared/request_handler_interface.hpp>
|
#include <bmrshared/request_handler_interface.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
|
||||||
using bmrshared::web::detail::internal_server;
|
using bmrshared::web::detail::internal_server;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -19,7 +19,6 @@ internal_server::internal_server(boost::asio::io_context& io_context,
|
||||||
unsigned int max_simultaneous_requests,
|
unsigned int max_simultaneous_requests,
|
||||||
request_handler_interface& handler)
|
request_handler_interface& handler)
|
||||||
: m_io_context(io_context)
|
: m_io_context(io_context)
|
||||||
, m_strand(io_context)
|
|
||||||
, m_endPoint(listen_endpoint)
|
, m_endPoint(listen_endpoint)
|
||||||
, m_incoming_request_timeout(incoming_request_timeout)
|
, m_incoming_request_timeout(incoming_request_timeout)
|
||||||
, m_max_simultaneous_requests(max_simultaneous_requests)
|
, m_max_simultaneous_requests(max_simultaneous_requests)
|
||||||
|
|
@ -47,18 +46,11 @@ void internal_server::async_accept()
|
||||||
// Start accepting incoming connections asynchronously.
|
// Start accepting incoming connections asynchronously.
|
||||||
m_acceptor.async_accept(
|
m_acceptor.async_accept(
|
||||||
[weak_server = weak_from_this()](boost::system::error_code ec, boost::asio::ip::tcp::socket socket)
|
[weak_server = weak_from_this()](boost::system::error_code ec, boost::asio::ip::tcp::socket socket)
|
||||||
{
|
|
||||||
auto shared_server = weak_server.lock();
|
|
||||||
if(shared_server)
|
|
||||||
{
|
|
||||||
boost::asio::post(shared_server->m_strand, [weak_server, ec, s = std::make_shared<boost::asio::ip::tcp::socket>(std::move(socket))]
|
|
||||||
{
|
{
|
||||||
auto shared_server = weak_server.lock();
|
auto shared_server = weak_server.lock();
|
||||||
if (shared_server)
|
if (shared_server)
|
||||||
{
|
{
|
||||||
shared_server->do_accept(ec, std::move(*s));
|
shared_server->do_accept(ec, std::move(socket));
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -72,48 +64,46 @@ void internal_server::do_accept(boost::system::error_code ec, boost::asio::ip::t
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto sessionptr = std::make_shared<http_session>(std::move(socket));
|
m_sessions.push_back(std::make_shared<http_session>(std::move(socket)));
|
||||||
m_sessions.push_back(sessionptr);
|
|
||||||
}
|
|
||||||
async_accept();
|
|
||||||
session_process_queued();
|
session_process_queued();
|
||||||
|
async_accept();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void internal_server::session_process_queued()
|
void internal_server::session_process_queued()
|
||||||
{
|
{
|
||||||
// Determine how many outstanding requests we have.
|
|
||||||
std::size_t count_responding = 0;
|
|
||||||
for(const auto& session : m_sessions)
|
|
||||||
{
|
|
||||||
if (session->m_state == session_state::wait_response)
|
|
||||||
{
|
|
||||||
count_responding++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out all processed responses.
|
// Send out all processed responses.
|
||||||
for (auto& session : m_sessions)
|
for (auto& session : m_sessions)
|
||||||
{
|
{
|
||||||
if (session)
|
if (session)
|
||||||
{
|
{
|
||||||
http_session& s = *session;
|
http_session& s = *session;
|
||||||
auto& our = s.m_outstanding_request;
|
|
||||||
|
|
||||||
if (our && our->m_response_sender)
|
auto& our = s.m_outstanding_requests;
|
||||||
|
|
||||||
|
while (!our.empty() && our.front() && our.front()->m_response_sender)
|
||||||
{
|
{
|
||||||
(*our->m_response_sender)(s.stream);
|
auto& resp = our.front();
|
||||||
our.reset();
|
(*resp->m_response_sender)(s.stream);
|
||||||
s.m_state = session_state::queue_reading;
|
our.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine how many outstanding requests we have.
|
||||||
|
std::size_t count_responding = 0;
|
||||||
|
for(const auto& session : m_sessions)
|
||||||
|
{
|
||||||
|
count_responding += session->m_outstanding_requests.size();
|
||||||
|
}
|
||||||
|
|
||||||
// Allow for more outstanding requests unless we are already over the maximum.
|
// Allow for more outstanding requests unless we are already over the maximum.
|
||||||
if (count_responding < m_max_simultaneous_requests)
|
if (count_responding < m_max_simultaneous_requests)
|
||||||
{
|
{
|
||||||
for(auto& session : m_sessions)
|
for(auto& session : m_sessions)
|
||||||
{
|
{
|
||||||
if (!session)
|
if (!session || (count_responding > m_max_simultaneous_requests))
|
||||||
{
|
{
|
||||||
// do nothing.
|
// do nothing.
|
||||||
}
|
}
|
||||||
|
|
@ -126,19 +116,12 @@ void internal_server::session_process_queued()
|
||||||
s.stream.expires_after(m_incoming_request_timeout);
|
s.stream.expires_after(m_incoming_request_timeout);
|
||||||
|
|
||||||
auto handler = [weak_server = weak_from_this(), session](boost::beast::error_code ec, std::size_t size)
|
auto handler = [weak_server = weak_from_this(), session](boost::beast::error_code ec, std::size_t size)
|
||||||
{
|
|
||||||
auto shared_server = weak_server.lock();
|
|
||||||
if (shared_server)
|
|
||||||
{
|
|
||||||
boost::asio::post(shared_server->m_strand, [weak_server, session, ec, size]
|
|
||||||
{
|
{
|
||||||
auto shared_server = weak_server.lock();
|
auto shared_server = weak_server.lock();
|
||||||
if (shared_server)
|
if (shared_server)
|
||||||
{
|
{
|
||||||
shared_server->session_on_read(session, ec, size);
|
shared_server->session_on_read(session, ec, size);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
boost::beast::http::async_read(s.stream, s.buffer, *s.parser, handler);
|
boost::beast::http::async_read(s.stream, s.buffer, *s.parser, handler);
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +135,13 @@ void internal_server::session_on_read(std::shared_ptr<http_session> session,
|
||||||
{
|
{
|
||||||
http_session& s = *session;
|
http_session& s = *session;
|
||||||
|
|
||||||
if (ec)
|
if (ec == boost::beast::http::error::end_of_stream)
|
||||||
|
{
|
||||||
|
// End of stream. Shutdown and remove http session
|
||||||
|
s.stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||||
|
session_remove(session);
|
||||||
|
}
|
||||||
|
else if (ec)
|
||||||
{
|
{
|
||||||
// Any other kind of error, also shutdown and remove http_session
|
// Any other kind of error, also shutdown and remove http_session
|
||||||
s.stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
|
s.stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
|
||||||
|
|
@ -167,42 +156,29 @@ void internal_server::session_on_read(std::shared_ptr<http_session> session,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::shared_ptr<http_request> req = std::make_shared<http_request>();
|
std::shared_ptr<http_request> req = s.m_outstanding_requests.emplace(std::make_shared<http_request>());
|
||||||
s.m_outstanding_request = req;
|
|
||||||
|
|
||||||
// A regular HTTP request.
|
// A regular HTTP request.
|
||||||
auto finalize_response = [session, weak_server = weak_from_this(), req](const request_response::response_sender& rs)
|
auto finalize_response = [session, weak_server = weak_from_this(), req](const response_promise::response_sender& rs)
|
||||||
{
|
{
|
||||||
// We have a response, add it to our spot in the queue.
|
// We have a response, add it to our spot in the queue.
|
||||||
auto shared_server = weak_server.lock();
|
auto shared_server = weak_server.lock();
|
||||||
if (shared_server)
|
if (shared_server)
|
||||||
{
|
{
|
||||||
// Queue processing of the queue so we can continue.
|
// We have a response, add it to our spot in the queue.
|
||||||
boost::asio::post(shared_server->m_strand,
|
|
||||||
[shared_server, req, rs]
|
|
||||||
{
|
|
||||||
req->m_response_sender = rs;
|
req->m_response_sender = rs;
|
||||||
shared_server->session_process_queued();
|
|
||||||
});
|
// Queue processing of the queue so we can continue.
|
||||||
|
shared_server->m_io_context.post([shared_server]{shared_server->session_process_queued();});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto& address = session->stream.socket().remote_endpoint().address();
|
const auto& address = session->stream.socket().remote_endpoint().address();
|
||||||
const auto& request = session->parser->get();
|
m_handler.handle_request_http(address, session->parser->get(), response_promise(finalize_response));
|
||||||
|
|
||||||
auto rs = request_response(request, finalize_response);
|
s.m_state = session_state::queue_reading;
|
||||||
auto& internal = rs.create_response<boost::beast::http::response<boost::beast::http::string_body>>(boost::beast::http::status::internal_server_error, request.version());
|
|
||||||
internal.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING);
|
|
||||||
internal.set(boost::beast::http::field::content_type, "text/plain");
|
|
||||||
internal.prepare_payload();
|
|
||||||
internal.body() = "Internal server error.";
|
|
||||||
internal.keep_alive(request.keep_alive());
|
|
||||||
|
|
||||||
m_handler.handle_request_http(rs);
|
|
||||||
|
|
||||||
s.m_state = session_state::wait_response;
|
|
||||||
}
|
|
||||||
session_process_queued();
|
session_process_queued();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void internal_server::session_finalize_response(std::shared_ptr<http_session> session)
|
void internal_server::session_finalize_response(std::shared_ptr<http_session> session)
|
||||||
|
|
@ -216,11 +192,5 @@ void internal_server::session_finalize_response(std::shared_ptr<http_session> se
|
||||||
|
|
||||||
void internal_server::session_remove(std::shared_ptr<http_session> session)
|
void internal_server::session_remove(std::shared_ptr<http_session> session)
|
||||||
{
|
{
|
||||||
m_sessions.erase(std::remove_if(m_sessions.begin(),
|
m_sessions.erase(std::remove(m_sessions.begin(), m_sessions.end(), session), m_sessions.end());
|
||||||
m_sessions.end(),
|
|
||||||
[session](const std::shared_ptr<http_session>& s)
|
|
||||||
{
|
|
||||||
return session.get() == s.get();
|
|
||||||
}),
|
|
||||||
m_sessions.end());
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,43 +6,35 @@
|
||||||
// the Free Software Foundation.
|
// the Free Software Foundation.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <bmrshared/response_promise.hpp>
|
||||||
#include <bmrshared/server.hpp>
|
#include <bmrshared/server.hpp>
|
||||||
#include <bmrshared/request_response.hpp>
|
|
||||||
#include <boost/asio/io_context.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
#include <boost/asio/strand.hpp>
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace bmrshared::web {
|
namespace bmrshared::web {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
enum class session_state {
|
enum class session_state {
|
||||||
queue_reading,
|
queue_reading,
|
||||||
reading,
|
reading
|
||||||
wait_response
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_request
|
struct http_request
|
||||||
{
|
{
|
||||||
std::optional<request_response::response_sender> m_response_sender;
|
std::optional<response_promise::response_sender> m_response_sender;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_session {
|
struct http_session {
|
||||||
explicit http_session(boost::asio::ip::tcp::socket s)
|
explicit http_session(boost::asio::ip::tcp::socket s)
|
||||||
: stream(std::move(s))
|
: stream(std::move(s))
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
~http_session()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
boost::beast::tcp_stream stream;
|
boost::beast::tcp_stream stream;
|
||||||
boost::beast::flat_buffer buffer;
|
boost::beast::flat_buffer buffer;
|
||||||
boost::optional<boost::beast::http::request_parser<boost::beast::http::string_body>> parser;
|
boost::optional<boost::beast::http::request_parser<boost::beast::http::string_body>> parser;
|
||||||
session_state m_state = session_state::queue_reading;
|
session_state m_state;
|
||||||
std::shared_ptr<http_request> m_outstanding_request;
|
std::queue<std::shared_ptr<http_request>> m_outstanding_requests;
|
||||||
};
|
};
|
||||||
|
|
||||||
class internal_server final : public std::enable_shared_from_this<internal_server> {
|
class internal_server final : public std::enable_shared_from_this<internal_server> {
|
||||||
|
|
@ -72,7 +64,6 @@ namespace detail {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
boost::asio::io_context& m_io_context;
|
boost::asio::io_context& m_io_context;
|
||||||
boost::asio::io_context::strand m_strand;
|
|
||||||
boost::asio::ip::tcp::endpoint m_endPoint;
|
boost::asio::ip::tcp::endpoint m_endPoint;
|
||||||
std::chrono::seconds m_incoming_request_timeout;
|
std::chrono::seconds m_incoming_request_timeout;
|
||||||
unsigned int m_max_simultaneous_requests;
|
unsigned int m_max_simultaneous_requests;
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
// GNU Lesser General Public License v3.0
|
|
||||||
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
|
||||||
//
|
|
||||||
// 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 <bmrshared/request_response.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using bmrshared::web::request_response;
|
|
||||||
|
|
||||||
request_response::response_writer_interface::~response_writer_interface() = default;
|
|
||||||
|
|
||||||
request_response::internal_request_response::internal_request_response(request_type request, callback_on_finalize cb_finalize)
|
|
||||||
: m_request(std::move(request))
|
|
||||||
, m_cb_finalize(std::move(cb_finalize))
|
|
||||||
, m_response_writer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
request_response::internal_request_response::~internal_request_response()
|
|
||||||
{
|
|
||||||
if (m_response_writer)
|
|
||||||
{
|
|
||||||
std::shared_ptr<response_writer_interface> shared_writer(std::move(m_response_writer));
|
|
||||||
m_cb_finalize(
|
|
||||||
[shared_writer](boost::beast::tcp_stream& stream)
|
|
||||||
{
|
|
||||||
shared_writer->write_response(stream);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const request_response::request_type& request_response::internal_request_response::get_request() const
|
|
||||||
{
|
|
||||||
return m_request;
|
|
||||||
}
|
|
||||||
|
|
||||||
void request_response::internal_request_response::set_response_sender(std::unique_ptr<response_writer_interface> response_writer)
|
|
||||||
{
|
|
||||||
m_response_writer = std::move(response_writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
request_response::request_response(request_type request, callback_on_finalize cb_finalize)
|
|
||||||
: m_internal(std::make_unique<internal_request_response>(std::move(request), std::move(cb_finalize)))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
request_response::~request_response() = default;
|
|
||||||
|
|
||||||
const request_response::request_type& request_response::get_request() const
|
|
||||||
{
|
|
||||||
return m_internal->get_request();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// GNU Lesser General Public License v3.0
|
||||||
|
// Copyright (c) 2025 Bart Beumer <bart@4beumer.nl>
|
||||||
|
//
|
||||||
|
// 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 <bmrshared/response_promise.hpp>
|
||||||
|
|
||||||
|
using bmrshared::web::response_promise;
|
||||||
|
|
||||||
|
response_promise::response_promise(callback_on_response cbOnResponse)
|
||||||
|
: m_call_on_response(std::move(cbOnResponse))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
response_promise::~response_promise()
|
||||||
|
{
|
||||||
|
if (m_response_writer)
|
||||||
|
{
|
||||||
|
m_call_on_response(
|
||||||
|
[r = m_response_writer](boost::beast::tcp_stream& stream)
|
||||||
|
{
|
||||||
|
r->write_response(stream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps
|
||||||
|
|
||||||
class HelloConan(ConanFile):
|
class HelloConan(ConanFile):
|
||||||
settings = "os", "compiler", "build_type", "arch"
|
settings = "os", "compiler", "build_type", "arch"
|
||||||
requires = "boost/1.89.0", "gtest/1.14.0", "libmagic/5.45", "freetype/2.13.3", "libjpeg/9f"
|
requires = "boost/1.84.0", "gtest/1.14.0", "libmagic/5.45", "freetype/2.13.3", "libjpeg/9f"
|
||||||
generators = "CMakeDeps"
|
generators = "CMakeDeps"
|
||||||
build_policy = "*"
|
build_policy = "*"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue