diff --git a/example/server/handler.cpp b/example/server/handler.cpp
index 9e4a086..72a3bef 100644
--- a/example/server/handler.cpp
+++ b/example/server/handler.cpp
@@ -67,8 +67,6 @@ make_http_date()
return std::string(buf);
}
-//------------------------------------------------
-
static
void
prepare_error(
@@ -105,302 +103,13 @@ prepare_error(
body = ss.str();
}
-//------------------------------------------------
-
-// Return a reasonable mime type based on the extension of a file.
-static
-core::string_view
-get_extension(
- core::string_view path) noexcept
-{
- auto const pos = path.rfind(".");
- if( pos == core::string_view::npos)
- return core::string_view();
- return path.substr(pos);
-}
-
-static
-core::string_view
-mime_type(
- core::string_view path)
-{
- using urls::grammar::ci_is_equal;
- auto ext = get_extension(path);
- if(ci_is_equal(ext, ".htm")) return "text/html";
- if(ci_is_equal(ext, ".html")) return "text/html";
- if(ci_is_equal(ext, ".php")) return "text/html";
- if(ci_is_equal(ext, ".css")) return "text/css";
- if(ci_is_equal(ext, ".txt")) return "text/plain";
- if(ci_is_equal(ext, ".js")) return "application/javascript";
- if(ci_is_equal(ext, ".json")) return "application/json";
- if(ci_is_equal(ext, ".xml")) return "application/xml";
- if(ci_is_equal(ext, ".swf")) return "application/x-shockwave-flash";
- if(ci_is_equal(ext, ".flv")) return "video/x-flv";
- if(ci_is_equal(ext, ".png")) return "image/png";
- if(ci_is_equal(ext, ".jpe")) return "image/jpeg";
- if(ci_is_equal(ext, ".jpeg")) return "image/jpeg";
- if(ci_is_equal(ext, ".jpg")) return "image/jpeg";
- if(ci_is_equal(ext, ".gif")) return "image/gif";
- if(ci_is_equal(ext, ".bmp")) return "image/bmp";
- if(ci_is_equal(ext, ".ico")) return "image/vnd.microsoft.icon";
- if(ci_is_equal(ext, ".tiff")) return "image/tiff";
- if(ci_is_equal(ext, ".tif")) return "image/tiff";
- if(ci_is_equal(ext, ".svg")) return "image/svg+xml";
- if(ci_is_equal(ext, ".svgz")) return "image/svg+xml";
- return "application/text";
-}
-
-// Append an HTTP rel-path to a local filesystem path.
-// The returned path is normalized for the platform.
-static
-void
-path_cat(
- std::string& result,
- core::string_view prefix,
- urls::segments_view suffix)
-{
- result = prefix;
-
-#ifdef BOOST_MSVC
- char constexpr path_separator = '\\';
-#else
- char constexpr path_separator = '/';
-#endif
- if( result.back() == path_separator)
- result.resize(result.size() - 1); // remove trailing
-#ifdef BOOST_MSVC
- for(auto& c : result)
- if( c == '/')
- c = path_separator;
-#endif
- for(auto const& seg : suffix)
- {
- result.push_back(path_separator);
- result.append(seg);
- }
-}
-
-//------------------------------------------------
-
-static
-bool
-make_error_response(
- http_proto::status code,
- http_proto::request_base const& req,
- http_proto::response& res,
- http_proto::serializer& sr)
-{
- std::string body;
- prepare_error(res, body, code, req);
- res.set_payload_size(body.size());
-#if 0
- auto rv = urls::parse_authority(
- req.value_or(http_proto::field::host, ""));
- core::string_view host;
- if(rv.has_value())
- host = rv->buffer();
- else
- host = "";
-
- std::string s;
- s = "\n";
- s += "
\n";
- s += "";
- s += std::to_string(static_cast<
- std::underlying_type<
- http_proto::status>::type>(code));
- s += " ";
- s += http_proto::obsolete_reason(code);
- s += "\n";
- s += "\n";
- s += "";
- s += http_proto::obsolete_reason(code);
- s += "
\n";
- if(code == http_proto::status::not_found)
- {
- s += "The requested URL ";
- s += req.target();
- s += " was not found on this server.
\n";
- }
- s += "
\n";
- s += "Boost.Http.IO/1.0b (Win10) Server at ";
- s += rv->host_address();
- s += " Port ";
- s += rv->port();
- s += "\n";
- s += "\n";
- res.set_start_line(code, res.version());
- res.set_keep_alive(req.keep_alive());
- res.set_payload_size(s.size());
- res.append(http_proto::field::content_type,
- "text/html; charset=iso-8859-1");
- res.append(http_proto::field::date,
- "Mon, 12 Dec 2022 03:26:32 GMT");
- res.append(http_proto::field::server,
- "Boost.Http.IO/1.0b (Win10)");
-#endif
-
- sr.start(res, http_proto::string_body(
- std::move(body)));
-
- return false;
-}
-
-static
-bool
-service_unavailable(
- http_proto::response& res,
- http_proto::serializer& sr,
- http_proto::request_base const& req)
-{
- auto const code = http_proto::status::service_unavailable;
- auto rv = urls::parse_authority( req.value_or( http_proto::field::host, "" ) );
- core::string_view host;
- if(rv.has_value())
- host = rv->buffer();
- else
- host = "";
-
- std::string s;
- s = "\n";
- s += "\n";
- s += "";
- s += std::to_string(static_cast<
- std::underlying_type<
- http_proto::status>::type>(code));
- s += " ";
- s += http_proto::obsolete_reason(code);
- s += "\n";
- s += "\n";
- s += "";
- s += http_proto::obsolete_reason(code);
- s += "
\n";
- s += "
\n";
- s += "Boost.Http.IO/1.0b (Win10) Server at ";
- s += rv->host_address();
- s += " Port ";
- s += rv->port();
- s += "\n";
- s += "\n";
-
- res.set_start_line(code, res.version());
- res.set_keep_alive(false);
- res.set_payload_size(s.size());
- res.append(http_proto::field::content_type, "text/html; charset=iso-8859-1");
- //res.append(http_proto::field::date, "Mon, 12 Dec 2022 03:26:32 GMT" );
- res.append(http_proto::field::server, "Boost.Http.Io");
-
- sr.start(
- res,
- http_proto::string_body(
- std::move(s)));
- return false;
-}
-
-//------------------------------------------------
-
-bool
-file_responder::
-operator()(
- Request& req,
- Response& res) const
-{
- if(req.is_shutting_down)
- {
- service_unavailable(
- res.res, res.sr, req.req);
- return true;
- }
-
-#if 0
- // Returns a server error response
- auto const server_error =
- [&req](beast::string_view what)
- {
- http::response res{http::status::internal_server_error, req.version()};
- res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
- res.set(http::field::content_type, "text/html");
- res.keep_alive(req.keep_alive());
- res.body() = "An error occurred: '" + std::string(what) + "'";
- res.prepare_payload();
- return res;
- };
-#endif
-
- // Request path must be absolute and not contain "..".
- if( req.req.target().empty() ||
- req.req.target()[0] != '/' ||
- req.req.target().find("..") != core::string_view::npos)
- {
- make_error_response(http_proto::status::bad_request,
- req.req, res.res, res.sr);
- return true;
- }
-
- // Build the path to the requested file
- std::string path;
- path_cat(path, doc_root_, req.path);
- if(req.pr.get().target().back() == '/')
- {
- path.push_back('/');
- path.append("index.html");
- }
-
- // Attempt to open the file
- system::error_code ec;
- http_proto::file f;
- std::uint64_t size = 0;
- f.open(path.c_str(), http_proto::file_mode::scan, ec);
- if(! ec.failed())
- size = f.size(ec);
- if(! ec.failed())
- {
- res.res.set_start_line(
- http_proto::status::ok,
- req.req.version());
- res.res.set(http_proto::field::server, "Boost");
- res.res.set_keep_alive(req.req.keep_alive());
- res.res.set_payload_size(size);
-
- auto mt = mime_type(get_extension(path));
- res.res.append(
- http_proto::field::content_type, mt);
-
- res.sr.start(
- res.res, std::move(f), size);
- return true;
- }
-
- if(ec == system::errc::no_such_file_or_directory)
- {
- make_error_response(
- http_proto::status::not_found,
- req.req, res.res, res.sr);
- return true;
- }
-
- // ec.message()?
- make_error_response(
- http_proto::status::internal_server_error,
- req.req, res.res, res.sr);
- return true;
-}
-
-//------------------------------------------------
-
-bool
+auto
https_redirect_responder::
operator()(
Request& req,
- Response& res) const
+ Response& res) const ->
+ system::error_code
{
- if(req.is_shutting_down)
- {
- service_unavailable(
- res.res, res.sr, req.req);
- return true;
- }
-
std::string body;
prepare_error(res.res, body,
http_proto::status::moved_permanently, req.req);
@@ -410,7 +119,7 @@ operator()(
res.res.append(http_proto::field::location, u1.buffer());
res.sr.start(res.res,
http_proto::string_body( std::move(body)));
- return true;
+ return {};
}
} // beast2
diff --git a/example/server/handler.hpp b/example/server/handler.hpp
index 2251dc9..cb918bf 100644
--- a/example/server/handler.hpp
+++ b/example/server/handler.hpp
@@ -12,38 +12,13 @@
#include
#include
-#include
-#include
-#include
-#include
-#include
namespace boost {
namespace beast2 {
-using router_type = router;
-
-//------------------------------------------------
-
struct https_redirect_responder
{
- bool operator()(Request&, Response&) const;
-};
-
-//------------------------------------------------
-
-struct file_responder
-{
- file_responder(
- core::string_view doc_root)
- : doc_root_(doc_root)
- {
- }
-
- bool operator()(Request&, Response&) const;
-
-private:
- std::string doc_root_;
+ system::error_code operator()(Request&, Response&) const;
};
} // beast2
diff --git a/example/server/http_responder.hpp b/example/server/http_responder.hpp
index e9268a7..e15af42 100644
--- a/example/server/http_responder.hpp
+++ b/example/server/http_responder.hpp
@@ -72,7 +72,7 @@ class http_responder
}
void on_read(
- system::error_code const& ec,
+ system::error_code ec,
std::size_t bytes_transferred)
{
(void)bytes_transferred;
@@ -101,8 +101,9 @@ class http_responder
};
// invoke handlers for the route
- auto handled = rr_(req, res);
- if(! handled)
+ ec = rr_(req, res);
+ BOOST_ASSERT(! ec.failed());
+ if(ec.failed())
{
// give a default error response?
}
diff --git a/example/server/main.cpp b/example/server/main.cpp
index cde9373..0fb5cc0 100644
--- a/example/server/main.cpp
+++ b/example/server/main.cpp
@@ -10,7 +10,9 @@
#include "certificate.hpp"
#include "worker_ssl.hpp"
#include
+#include
#include
+#include
#include
#include
#include
@@ -25,6 +27,11 @@
namespace boost {
namespace beast2 {
+system::error_code fh( Request&, Response& )
+{
+ return {};
+}
+
int server_main( int argc, char* argv[] )
{
try
@@ -86,6 +93,16 @@ int server_main( int argc, char* argv[] )
router_type app;
+ // common response fields
+ app.use(
+ [](Request& req, Response& res)
+ {
+ res.res.set(http_proto::field::server, "Boost");
+ res.res.set_keep_alive(req.req.keep_alive());
+ return error::next;
+ });
+
+#if 0
// redirect HTTP to HTTPS
app.use(
[](Request& req, Response& res)
@@ -93,15 +110,21 @@ int server_main( int argc, char* argv[] )
if(! req.port.is_ssl)
{
https_redirect_responder()(req, res);
- return true;
+ return {};
}
- return false;
+ return error::next;
});
+#endif
// static route for website
- app.get("/",
- file_responder{ doc_root });
-
+ app.use("/", serve_static( doc_root ));
+ app.use("/alt", serve_static( doc_root ));
+ app.use("/test", fh);
+ app.err(
+ [](Request& req, Response& res, system::error_code const& ec)
+ {
+ return system::error_code{};
+ });
using workers_type =
workers< executor_type, worker_ssl >;
diff --git a/include/boost/beast2/endpoint.hpp b/include/boost/beast2/endpoint.hpp
index ab63bbe..3361a60 100644
--- a/include/boost/beast2/endpoint.hpp
+++ b/include/boost/beast2/endpoint.hpp
@@ -29,10 +29,24 @@ namespace beast2 {
class endpoint
{
public:
- BOOST_BEAST2_DECL
- ~endpoint();
+ ~endpoint()
+ {
+ switch(kind_)
+ {
+ case urls::host_type::ipv4:
+ ipv4_.~ipv4_address();
+ break;
+ case urls::host_type::ipv6:
+ ipv6_.~ipv6_address();
+ break;
+ default:
+ break;
+ }
+ }
- endpoint() = default;
+ endpoint() noexcept
+ {
+ }
BOOST_BEAST2_DECL
endpoint(
@@ -108,7 +122,6 @@ class endpoint
urls::ipv4_address ipv4_;
urls::ipv6_address ipv6_;
};
-
};
} // beast2
diff --git a/include/boost/beast2/error.hpp b/include/boost/beast2/error.hpp
new file mode 100644
index 0000000..580756e
--- /dev/null
+++ b/include/boost/beast2/error.hpp
@@ -0,0 +1,91 @@
+//
+// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/cppalliance/beast2
+//
+
+#ifndef BOOST_BEAST2_ERROR_HPP
+#define BOOST_BEAST2_ERROR_HPP
+
+#include
+#include
+#include
+#include
+#include
+
+namespace boost {
+namespace beast2 {
+
+/** Error codes
+*/
+enum class error
+{
+ success = 0,
+
+ // Routing
+
+ /** The route handler did not satisfy the request
+ */
+ next,
+
+ /** The route handler is detaching from the session
+ */
+ detach,
+
+ /** The route handler wants to close the connection
+ */
+ close
+};
+
+} // beast2
+
+namespace system {
+template<>
+struct is_error_code_enum<
+ ::boost::beast2::error>
+{
+ static bool const value = true;
+};
+} // system
+
+namespace beast2 {
+
+namespace detail {
+struct BOOST_SYMBOL_VISIBLE
+ error_cat_type
+ : system::error_category
+{
+ BOOST_BEAST2_DECL const char* name(
+ ) const noexcept override;
+ BOOST_BEAST2_DECL std::string message(
+ int) const override;
+ BOOST_BEAST2_DECL char const* message(
+ int, char*, std::size_t
+ ) const noexcept override;
+ BOOST_SYSTEM_CONSTEXPR error_cat_type()
+ : error_category(0x515eb9dbd1314d96 )
+ {
+ }
+};
+BOOST_BEAST2_DECL extern error_cat_type error_cat;
+} // detail
+
+inline
+BOOST_SYSTEM_CONSTEXPR
+system::error_code
+make_error_code(
+ error ev) noexcept
+{
+ return system::error_code{
+ static_cast::type>(ev),
+ detail::error_cat};
+}
+
+} // beast2
+} // boost
+
+#endif
diff --git a/include/boost/beast2/server/route_params.hpp b/include/boost/beast2/server/route_params.hpp
index 00c4ec9..0c9d4ef 100644
--- a/include/boost/beast2/server/route_params.hpp
+++ b/include/boost/beast2/server/route_params.hpp
@@ -11,10 +11,12 @@
#define BOOST_BEAST2_SERVER_ROUTE_PARAMS_HPP
#include
+#include
#include
#include
#include
#include
+#include // for return value
namespace boost {
namespace beast2 {
@@ -61,6 +63,8 @@ struct AsioResponse : Response
}
};
+using router_type = router;
+
} // beast2
} // boost
diff --git a/include/boost/beast2/server/router.hpp b/include/boost/beast2/server/router.hpp
index 0ae863c..8995d52 100644
--- a/include/boost/beast2/server/router.hpp
+++ b/include/boost/beast2/server/router.hpp
@@ -22,33 +22,6 @@ namespace beast2 {
struct Request;
-//------------------------------------------------
-
-#if 0
-template
-struct of_type_t
-{
- using type = T;
-};
-
-namespace detail {
-template
-struct of_type_impl
-{
- static of_type_t const value;
-};
-template
-of_type_t const
-of_type_impl::value = of_type_t{};
-} // detail
-
-template
-constexpr of_type_t const& of_type =
- detail::of_type_impl::value;
-#endif
-
-//------------------------------------------------
-
template
class router;
@@ -57,39 +30,155 @@ class router;
class router_base
{
protected:
+ template friend class fluent_route;
+
+ struct entry;
+ struct impl;
+
struct BOOST_SYMBOL_VISIBLE
any_handler
{
BOOST_BEAST2_DECL
virtual ~any_handler();
- virtual bool operator()(
+ virtual system::error_code operator()(
void* req, void* res) const = 0;
};
+ struct BOOST_SYMBOL_VISIBLE
+ any_errfn
+ {
+ BOOST_BEAST2_DECL
+ virtual ~any_errfn();
+
+ virtual system::error_code operator()(void* req,
+ void* res, system::error_code const&) const = 0;
+ };
+
using handler_ptr = std::unique_ptr;
+ using errfn_ptr = std::unique_ptr;
+
+ // wrapper for route handlers
+ template
+ struct handler_impl : any_handler
+ {
+ typename std::decay::type h;
- BOOST_BEAST2_DECL
- router_base(
+ template
+ handler_impl(Args&&... args)
+ : h(std::forward(args)...)
+ {
+ }
+
+ system::error_code operator()(
+ void* req, void* res) const override
+ {
+ return h(
+ *reinterpret_cast(req),
+ *reinterpret_cast(res));
+ }
+ };
+
+ // wrapper for error handling functions
+ template
+ struct errfn_impl : any_errfn
+ {
+ typename std::decay::type h;
+
+ template
+ errfn_impl(Args&&... args)
+ : h(std::forward(args)...)
+ {
+ }
+
+ system::error_code operator()(void* req, void* res,
+ system::error_code const& ec) const override
+ {
+ return h(*reinterpret_cast(req),
+ *reinterpret_cast(res), ec);
+ }
+ };
+
+ BOOST_BEAST2_DECL router_base(
http_proto::method(*)(void*),
urls::segments_encoded_view&(*)(void*));
+ BOOST_BEAST2_DECL system::error_code invoke(void*, void*) const;
+ BOOST_BEAST2_DECL void append(bool, http_proto::method,
+ core::string_view, handler_ptr);
+ BOOST_BEAST2_DECL void append_err(errfn_ptr);
+ //void append(bool, http_proto::method, core::string_view) {}
- BOOST_BEAST2_DECL
- void use(handler_ptr);
+ std::shared_ptr impl_;
+};
- BOOST_BEAST2_DECL
- void insert(
- http_proto::method,
- core::string_view,
- handler_ptr);
+//------------------------------------------------
- BOOST_BEAST2_DECL
- bool invoke(void*, void*) const;
+template
+class fluent_route
+{
+ static constexpr http_proto::method all_methods =
+ http_proto::method::unknown;
- struct entry;
- struct impl;
+ auto add(http_proto::method method) ->
+ fluent_route
+ {
+ return *this;
+ }
- std::shared_ptr impl_;
+public:
+ // note: express does not offer Route::use()
+
+ template
+ auto add(
+ http_proto::method method,
+ H0&& h0, HN&&... hn) ->
+ fluent_route
+ {
+ r_.append(false, method, pat_, typename
+ router_base::handler_ptr(new typename
+ router_base::handler_impl(std::forward(h0))));
+ r_.append(false, method, pat_, std::forward(hn)...);
+ return *this;
+ }
+
+ template
+ auto all(HN&&... hn) ->
+ fluent_route
+ {
+ return add(all_methods, std::forward(hn)...);
+ }
+
+ template
+ auto get(H0&& h0, HN&&... hn) ->
+ fluent_route
+ {
+ return add(
+ http_proto::method::get,
+ std::forward(h0),
+ std::forward(hn)...);
+ }
+
+ template
+ auto post(HN&&... hn) ->
+ fluent_route
+ {
+ return add(http_proto::method::post,
+ std::forward(hn)...);
+ }
+
+private:
+ friend class router;
+ fluent_route(
+ router_base& r,
+ core::string_view pat)
+ : r_(r)
+ , pat_(pat)
+ {
+ }
+
+ router_base& r_;
+ core::string_view pat_;
};
//------------------------------------------------
@@ -101,7 +190,12 @@ template<
class Request = beast2::Request>
class router : public router_base
{
+ static constexpr http_proto::method all_methods =
+ http_proto::method::unknown;
+
public:
+ /** Constructor
+ */
router()
: router_base(
[](void* req) -> http_proto::method
@@ -115,73 +209,131 @@ class router : public router_base
{
}
- /** Add a global handler
+ /** Add a global middleware
+ The handler will run for every request.
*/
- template
- router_base&
- use(Handler&& h)
+ template::value>::type
+ >
+ auto use(H0&& h0, HN&&... hn) ->
+ router&
{
- router_base::use(handler_ptr(
- new handler_impl(
- std::forward(h))));
+ append(true, all_methods, "/",
+ std::forward(h0),
+ std::forward(hn)...);
return *this;
}
- /** Add a GET handler
+ /** Add a mounted middleware
+ The handler will run for every request matching the given prefix.
*/
- template
- void
- get(
+ template
+ auto use(core::string_view pattern,
+ H0&& h0, HN... hn) ->
+ router&
+ {
+ append(true, all_methods, pattern,
+ std::forward(h0),
+ std::forward(hn)...);
+ return *this;
+ }
+
+ template
+ auto add(
+ http_proto::method method,
core::string_view pattern,
- Handler&& h)
+ H0&& h0, HN&&... hn) ->
+ fluent_route&
{
- insert(http_proto::method::get,
- pattern, std::forward(h));
+ return fluent_route(*this,
+ pattern).add(method, std::forward<
+ H0>(h0), std::forward(hn)...);
}
- /** Add a handler to match a method and pattern
+ /** Add an error handler
*/
- template
- void
- insert(
- http_proto::method method,
+ template
+ auto err(
+ H0&& h0, HN&&... hn) ->
+ router&
+ {
+ append_err(
+ std::forward(h0),
+ std::forward(hn)...);
+ return *this;
+ }
+
+ /** Add a route handler matching all methods and the given pattern
+ The handler will run for every request matching the entire pattern.
+ */
+ template
+ auto all(
+ core::string_view pattern,
+ H0&& h0, HN&&... hn) ->
+ fluent_route
+ {
+ return add(all_methods, pattern,
+ std::forward(h0), std::forward(hn)...);
+ }
+
+ /** Add a GET handler
+ */
+ template
+ auto get(
+ core::string_view pattern,
+ H0&& h0, HN&&... hn) ->
+ fluent_route
+ {
+ return add(http_proto::method::get, pattern,
+ std::forward(h0), std::forward(hn)...);
+ }
+
+ template
+ auto post(
core::string_view pattern,
- Handler&& h)
+ H0&& h0, HN&&... hn) ->
+ fluent_route
{
- router_base::insert(method, pattern,
- handler_ptr(new handler_impl(
- std::forward(h))));
+ return add(http_proto::method::post, pattern,
+ std::forward(h0), std::forward(hn)...);
}
- bool operator()(
+ system::error_code operator()(
Request& req, Response& res) const
{
return invoke(&req, &res);
}
private:
- template
- struct handler_impl : any_handler
+ void append(bool, http_proto::method,
+ core::string_view ) const noexcept
{
- template
- handler_impl(Args&&... args)
- : h(std::forward(args)...)
- {
- }
+ }
+
+ template
+ void append(bool prefix, http_proto::method method,
+ core::string_view pat, H0&& h, HN&&... hn)
+ {
+ router_base::append(prefix, method, pat,
+ handler_ptr(new handler_impl(
+ std::forward(h))));
+ append(prefix, method, pat, std::forward(hn)...);
+ }
+
+ void append_err() const noexcept
+ {
+ }
+
+ template
+ void append_err(H0&& h, HN&&... hn)
+ {
+ router_base::append_err(errfn_ptr(new
+ errfn_impl(
+ std::forward(h))));
+ append_err(std::forward(hn)...);
+ }
- private:
- using handler_type = typename
- std::decay::type;
- handler_type h;
- //static_assert(std::is_invocable<
- //Handler, Response&, Request&>::value, "");
- bool operator()(void* req, void* res) const override
- {
- return h(
- *reinterpret_cast(req),
- *reinterpret_cast(res));
- }
- };
};
//------------------------------------------------
diff --git a/include/boost/beast2/server/serve_static.hpp b/include/boost/beast2/server/serve_static.hpp
new file mode 100644
index 0000000..869a3f7
--- /dev/null
+++ b/include/boost/beast2/server/serve_static.hpp
@@ -0,0 +1,178 @@
+//
+// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/cppalliance/beast2
+//
+
+#ifndef BOOST_BEAST2_SERVER_SERVE_STATIC_HPP
+#define BOOST_BEAST2_SERVER_SERVE_STATIC_HPP
+
+#include
+#include
+#include
+
+namespace boost {
+namespace beast2 {
+
+/** A route handler which serves static files from a document root
+
+ This handler operates similary to the npm package serve-static.
+*/
+struct serve_static
+{
+ /** Policy for handling dotfiles
+ */
+ enum class dotfiles_policy
+ {
+ allow,
+ deny,
+ ignore
+ };
+
+ /** Options for the static file server
+ */
+ struct options
+ {
+ /** How to handle dotfiles
+
+ Dotfiles are files or directories whose names begin with a dot (.)
+ The default is to ignore dotfiles.
+ */
+ dotfiles_policy dotfiles = dotfiles_policy::ignore;
+
+ // VFALCO extensions fallbacks vector
+
+ // VFALCO vector of index file names
+
+ /** Maximum cache age in milliseconds
+ */
+ // VFALCO
+ // std::chrono::duration max_age = std::chrono::milliseconds(0); ?
+ std::size_t max_age_ms = 0;
+
+ // VFALCO set_headers callback
+
+ /** Enable accepting range requests.
+
+ When this is false, the "Accept-Ranges" field will not be
+ sent, and any "Range" field in the request will be ignored.
+ */
+ bool accept_ranges = true;
+
+ /** Enable sending cache-control headers.
+
+ When this is set to `false`, the @ref immutable
+ and @ref max_age options are ignored.
+ */
+ bool cache_control = true;
+
+ /** Enable etag header generation.
+ */
+ bool etag = true;
+
+ /** Treat client errors as unhandled requests.
+
+ When this value is `true`, all error codes will be
+ treated as if unhandled. Otherwise, errors (including
+ file not found) will go through the error handling routes.
+
+ Typically true is desired such that multiple physical
+ directories can be mapped to the same web address or for
+ routes to fill in non-existent files.
+
+ The value false can be used if this handler is mounted
+ at a path that is designed to be strictly a single file system
+ directory, which allows for short-circuiting 404s for less
+ overhead. This handler will also reply to all methods.
+
+ @note This handler replies to all HTTP methods.
+ */
+ bool fallthrough = true;
+
+ /** Enable the immutable directive in cache control headers.
+
+ When this is true, the "immutable" directive will be
+ added to the "Cache-Control" field.
+ This indicates to clients that the resource will not
+ change during its freshness lifetime.
+ This is typically used when the filenames contain
+ a hash of the content, such as when using a build
+ tool which fingerprints static assets.
+ The @ref max_age value must also be set to a non-zero value.
+ */
+ bool immutable = false;
+
+ /** Enable a default index file for directory requests.
+ When a request is made for a directory path, such as
+ "/docs/", the file "index.html" will be served if it
+ exists within that directory.
+ */
+ bool index = true; // "index.html" default
+
+ /** Enable the "Last-Modified" header.
+
+ The file system's last modified value is used.
+ */
+ bool last_modified = true;
+
+ /** Enable redirection for directories missing a trailing slash.
+
+ When a request is made for a directory path without a trailing
+ slash, the client is redirected to the same path with the slash
+ appended. This is useful for relative links to work correctly
+ in browsers.
+ For example, a request for `/docs` when `/docs/index.html` exists
+ will be redirected to `/docs/`.
+ @note This requires that the client accepts redirections.
+ */
+ bool redirect = true;
+ };
+
+ BOOST_BEAST2_DECL
+ ~serve_static();
+
+ /** Constructor
+ @param path The document root path
+ @param options The options to use
+ */
+ BOOST_BEAST2_DECL
+ serve_static(
+ core::string_view path,
+ options const& opt);
+
+ /** Constructor
+ @param path The document root path
+ */
+ explicit
+ serve_static(
+ core::string_view path)
+ : serve_static(path, options{})
+ {
+ }
+
+ /** Constructor
+ */
+ BOOST_BEAST2_DECL
+ serve_static(serve_static&&) noexcept;
+
+ /** Handle a request
+ @param req The request
+ @param res The response
+ @return `true` if the request was handled, `false` to
+ indicate the request was not handled.
+ */
+ BOOST_BEAST2_DECL
+ system::error_code operator()(Request&, Response&) const;
+
+private:
+ struct impl;
+ std::unique_ptr impl_;
+};
+
+} // beast2
+} // boost
+
+#endif
diff --git a/src/error.cpp b/src/error.cpp
new file mode 100644
index 0000000..465a044
--- /dev/null
+++ b/src/error.cpp
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// Official repository: https://github.com/cppalliance/beast2
+//
+
+#include
+#include
+#include
+
+namespace boost {
+namespace beast2 {
+
+namespace detail {
+
+const char*
+error_cat_type::
+name() const noexcept
+{
+ return "boost.beast2";
+}
+
+std::string
+error_cat_type::
+message(int code) const
+{
+ return message(code, nullptr, 0);
+}
+
+char const*
+error_cat_type::
+message(
+ int code,
+ char*,
+ std::size_t) const noexcept
+{
+ switch(static_cast(code))
+ {
+ case error::next: return "next";
+ case error::detach: return "detach";
+ case error::close: return "close";
+ default:
+ return "unknown";
+ }
+}
+
+//-----------------------------------------------
+
+// msvc 14.0 has a bug that warns about inability
+// to use constexpr construction here, even though
+// there's no constexpr construction
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+# pragma warning( push )
+# pragma warning( disable : 4592 )
+#endif
+
+#if defined(__cpp_constinit) && __cpp_constinit >= 201907L
+constinit error_cat_type error_cat;
+#else
+error_cat_type error_cat;
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+# pragma warning( pop )
+#endif
+
+} // detail
+
+} // beast2
+} // boost
diff --git a/src/server/endpoint.cpp b/src/server/endpoint.cpp
index da9c5c4..b9b712e 100644
--- a/src/server/endpoint.cpp
+++ b/src/server/endpoint.cpp
@@ -13,22 +13,6 @@
namespace boost {
namespace beast2 {
-endpoint::
-~endpoint()
-{
- switch(kind_)
- {
- case urls::host_type::ipv4:
- ipv4_.~ipv4_address();
- break;
- case urls::host_type::ipv6:
- ipv6_.~ipv6_address();
- break;
- default:
- break;
- }
-}
-
endpoint::
endpoint(
endpoint const& other) noexcept
diff --git a/src/server/router.cpp b/src/server/router.cpp
index cfffedc..318c325 100644
--- a/src/server/router.cpp
+++ b/src/server/router.cpp
@@ -9,7 +9,9 @@
#include "src/server/route_rule.hpp"
#include
-#include
+#include
+#include
+//#include
#include